added support for gravity settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
749     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
754     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
759     &li.bd_slime_eats_element_2,        EL_BD_ROCK
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
764     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
769     &li.bd_slime_eats_element_3,        EL_BD_NUT
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
774     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
775   },
776
777   {
778     EL_BD_ACID,                         -1,
779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
780     &li.bd_acid_eats_element,           EL_BD_SAND
781   },
782   {
783     EL_BD_ACID,                         -1,
784     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
785     &li.bd_acid_spread_rate,            3
786   },
787   {
788     EL_BD_ACID,                         -1,
789     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
790     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
791   },
792
793   {
794     EL_BD_BITER,                        -1,
795     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
796     &li.bd_biter_move_delay,            0
797   },
798   {
799     EL_BD_BITER,                        -1,
800     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
801     &li.bd_biter_eats_element,          EL_BD_DIAMOND
802   },
803
804   {
805     EL_BD_BLADDER,                      -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
808   },
809
810   {
811     EL_BD_EXPANDABLE_WALL_ANY,          -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.bd_change_expanding_wall,       FALSE
814   },
815   {
816     EL_BD_EXPANDABLE_WALL_ANY,          -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
818     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
819   },
820
821   {
822     EL_BD_REPLICATOR,                   -1,
823     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
824     &li.bd_replicators_active,          TRUE
825   },
826   {
827     EL_BD_REPLICATOR,                   -1,
828     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
829     &li.bd_replicator_create_delay,     4
830   },
831
832   {
833     EL_BD_CONVEYOR_LEFT,                -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
835     &li.bd_conveyor_belts_active,       TRUE
836   },
837   {
838     EL_BD_CONVEYOR_LEFT,                -1,
839     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
840     &li.bd_conveyor_belts_changed,      FALSE
841   },
842
843   {
844     EL_BD_WATER,                        -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
846     &li.bd_water_cannot_flow_down,      FALSE
847   },
848
849   {
850     EL_BD_NUT,                          -1,
851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
852     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
853   },
854
855   {
856     EL_BD_PNEUMATIC_HAMMER,             -1,
857     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
858     &li.bd_hammer_walls_break_delay,    5
859   },
860   {
861     EL_BD_PNEUMATIC_HAMMER,             -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
863     &li.bd_hammer_walls_reappear,       FALSE
864   },
865   {
866     EL_BD_PNEUMATIC_HAMMER,             -1,
867     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
868     &li.bd_hammer_walls_reappear_delay, 100
869   },
870
871   {
872     EL_BD_SKELETON,                     -1,
873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
874     &li.bd_num_skeletons_needed_for_pot, 5
875   },
876   {
877     EL_BD_SKELETON,                     -1,
878     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
879     &li.bd_skeleton_worth_num_diamonds, 0
880   },
881
882   {
883     EL_BD_CREATURE_SWITCH,              -1,
884     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
885     &li.bd_creatures_start_backwards,   FALSE
886   },
887   {
888     EL_BD_CREATURE_SWITCH,              -1,
889     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
890     &li.bd_creatures_turn_on_hatching,  FALSE
891   },
892   {
893     EL_BD_CREATURE_SWITCH,              -1,
894     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
895     &li.bd_creatures_auto_turn_delay,   0
896   },
897
898   {
899     EL_BD_GRAVITY_SWITCH,               -1,
900     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
901     &li.bd_gravity_direction,           GD_MV_DOWN
902   },
903   {
904     EL_BD_GRAVITY_SWITCH,               -1,
905     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
906     &li.bd_gravity_switch_active,       FALSE
907   },
908   {
909     EL_BD_GRAVITY_SWITCH,               -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
911     &li.bd_gravity_switch_delay,        10
912   },
913
914   {
915     EL_BD_SAND,                         -1,
916     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
917     &li.bd_sand_looks_like,             EL_BD_SAND
918   },
919
920   // (the following values are related to various game elements)
921
922   {
923     EL_EMERALD,                         -1,
924     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
925     &li.score[SC_EMERALD],              10
926   },
927
928   {
929     EL_DIAMOND,                         -1,
930     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
931     &li.score[SC_DIAMOND],              10
932   },
933
934   {
935     EL_BUG,                             -1,
936     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
937     &li.score[SC_BUG],                  10
938   },
939
940   {
941     EL_SPACESHIP,                       -1,
942     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
943     &li.score[SC_SPACESHIP],            10
944   },
945
946   {
947     EL_PACMAN,                          -1,
948     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
949     &li.score[SC_PACMAN],               10
950   },
951
952   {
953     EL_NUT,                             -1,
954     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
955     &li.score[SC_NUT],                  10
956   },
957
958   {
959     EL_DYNAMITE,                        -1,
960     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
961     &li.score[SC_DYNAMITE],             10
962   },
963
964   {
965     EL_KEY_1,                           -1,
966     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
967     &li.score[SC_KEY],                  10
968   },
969
970   {
971     EL_PEARL,                           -1,
972     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
973     &li.score[SC_PEARL],                10
974   },
975
976   {
977     EL_CRYSTAL,                         -1,
978     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
979     &li.score[SC_CRYSTAL],              10
980   },
981
982   // (amoeba values used by R'n'D game engine only)
983   {
984     EL_BD_AMOEBA,                       -1,
985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
986     &li.amoeba_content,                 EL_DIAMOND
987   },
988   {
989     EL_BD_AMOEBA,                       -1,
990     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
991     &li.amoeba_speed,                   10
992   },
993   {
994     EL_BD_AMOEBA,                       -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
996     &li.grow_into_diggable,             TRUE
997   },
998   // (amoeba values used by BD game engine only)
999   {
1000     EL_BD_AMOEBA,                       -1,
1001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1002     &li.bd_amoeba_wait_for_hatching,    FALSE
1003   },
1004   {
1005     EL_BD_AMOEBA,                       -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1007     &li.bd_amoeba_start_immediately,    TRUE
1008   },
1009   {
1010     EL_BD_AMOEBA,                       -1,
1011     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1012     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1013   },
1014   {
1015     EL_BD_AMOEBA,                       -1,
1016     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1017     &li.bd_amoeba_threshold_too_big,    200
1018   },
1019   {
1020     EL_BD_AMOEBA,                       -1,
1021     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1022     &li.bd_amoeba_slow_growth_time,     200
1023   },
1024   {
1025     EL_BD_AMOEBA,                       -1,
1026     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1027     &li.bd_amoeba_slow_growth_rate,     3
1028   },
1029   {
1030     EL_BD_AMOEBA,                       -1,
1031     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1032     &li.bd_amoeba_fast_growth_rate,     25
1033   },
1034   {
1035     EL_BD_AMOEBA,                       -1,
1036     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1037     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1038   },
1039   {
1040     EL_BD_AMOEBA,                       -1,
1041     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1042     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1043   },
1044
1045   {
1046     EL_BD_AMOEBA_2,                     -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1048     &li.bd_amoeba_2_threshold_too_big,  200
1049   },
1050   {
1051     EL_BD_AMOEBA_2,                     -1,
1052     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1053     &li.bd_amoeba_2_slow_growth_time,   200
1054   },
1055   {
1056     EL_BD_AMOEBA_2,                     -1,
1057     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1058     &li.bd_amoeba_2_slow_growth_rate,   3
1059   },
1060   {
1061     EL_BD_AMOEBA_2,                     -1,
1062     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1063     &li.bd_amoeba_2_fast_growth_rate,   25
1064   },
1065   {
1066     EL_BD_AMOEBA_2,                     -1,
1067     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1068     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1069   },
1070   {
1071     EL_BD_AMOEBA_2,                     -1,
1072     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1073     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1074   },
1075   {
1076     EL_BD_AMOEBA_2,                     -1,
1077     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1078     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1079   },
1080   {
1081     EL_BD_AMOEBA_2,                     -1,
1082     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1083     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1084   },
1085
1086   {
1087     EL_YAMYAM,                          -1,
1088     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1089     &li.yamyam_content,                 EL_ROCK, NULL,
1090     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1091   },
1092   {
1093     EL_YAMYAM,                          -1,
1094     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1095     &li.score[SC_YAMYAM],               10
1096   },
1097
1098   {
1099     EL_ROBOT,                           -1,
1100     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1101     &li.score[SC_ROBOT],                10
1102   },
1103   {
1104     EL_ROBOT,                           -1,
1105     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1106     &li.slurp_score,                    10
1107   },
1108
1109   {
1110     EL_ROBOT_WHEEL,                     -1,
1111     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1112     &li.time_wheel,                     10
1113   },
1114
1115   {
1116     EL_MAGIC_WALL,                      -1,
1117     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1118     &li.time_magic_wall,                10
1119   },
1120
1121   {
1122     EL_GAME_OF_LIFE,                    -1,
1123     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1124     &li.game_of_life[0],                2
1125   },
1126   {
1127     EL_GAME_OF_LIFE,                    -1,
1128     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1129     &li.game_of_life[1],                3
1130   },
1131   {
1132     EL_GAME_OF_LIFE,                    -1,
1133     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1134     &li.game_of_life[2],                3
1135   },
1136   {
1137     EL_GAME_OF_LIFE,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1139     &li.game_of_life[3],                3
1140   },
1141   {
1142     EL_GAME_OF_LIFE,                    -1,
1143     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1144     &li.use_life_bugs,                  FALSE
1145   },
1146
1147   {
1148     EL_BIOMAZE,                         -1,
1149     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1150     &li.biomaze[0],                     2
1151   },
1152   {
1153     EL_BIOMAZE,                         -1,
1154     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1155     &li.biomaze[1],                     3
1156   },
1157   {
1158     EL_BIOMAZE,                         -1,
1159     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1160     &li.biomaze[2],                     3
1161   },
1162   {
1163     EL_BIOMAZE,                         -1,
1164     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1165     &li.biomaze[3],                     3
1166   },
1167
1168   {
1169     EL_TIMEGATE_SWITCH,                 -1,
1170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1171     &li.time_timegate,                  10
1172   },
1173
1174   {
1175     EL_LIGHT_SWITCH_ACTIVE,             -1,
1176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1177     &li.time_light,                     10
1178   },
1179
1180   {
1181     EL_SHIELD_NORMAL,                   -1,
1182     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1183     &li.shield_normal_time,             10
1184   },
1185   {
1186     EL_SHIELD_NORMAL,                   -1,
1187     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1188     &li.score[SC_SHIELD],               10
1189   },
1190
1191   {
1192     EL_SHIELD_DEADLY,                   -1,
1193     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1194     &li.shield_deadly_time,             10
1195   },
1196   {
1197     EL_SHIELD_DEADLY,                   -1,
1198     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1199     &li.score[SC_SHIELD],               10
1200   },
1201
1202   {
1203     EL_EXTRA_TIME,                      -1,
1204     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1205     &li.extra_time,                     10
1206   },
1207   {
1208     EL_EXTRA_TIME,                      -1,
1209     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1210     &li.extra_time_score,               10
1211   },
1212
1213   {
1214     EL_TIME_ORB_FULL,                   -1,
1215     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1216     &li.time_orb_time,                  10
1217   },
1218   {
1219     EL_TIME_ORB_FULL,                   -1,
1220     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1221     &li.use_time_orb_bug,               FALSE
1222   },
1223
1224   {
1225     EL_SPRING,                          -1,
1226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1227     &li.use_spring_bug,                 FALSE
1228   },
1229
1230   {
1231     EL_EMC_ANDROID,                     -1,
1232     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1233     &li.android_move_time,              10
1234   },
1235   {
1236     EL_EMC_ANDROID,                     -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1238     &li.android_clone_time,             10
1239   },
1240   {
1241     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1242     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1243     &li.android_clone_element[0],       EL_EMPTY, NULL,
1244     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1245   },
1246   {
1247     EL_EMC_ANDROID,                     -1,
1248     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1249     &li.android_clone_element[0],       EL_EMPTY, NULL,
1250     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1251   },
1252
1253   {
1254     EL_EMC_LENSES,                      -1,
1255     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1256     &li.lenses_score,                   10
1257   },
1258   {
1259     EL_EMC_LENSES,                      -1,
1260     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1261     &li.lenses_time,                    10
1262   },
1263
1264   {
1265     EL_EMC_MAGNIFIER,                   -1,
1266     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1267     &li.magnify_score,                  10
1268   },
1269   {
1270     EL_EMC_MAGNIFIER,                   -1,
1271     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1272     &li.magnify_time,                   10
1273   },
1274
1275   {
1276     EL_EMC_MAGIC_BALL,                  -1,
1277     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1278     &li.ball_time,                      10
1279   },
1280   {
1281     EL_EMC_MAGIC_BALL,                  -1,
1282     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1283     &li.ball_random,                    FALSE
1284   },
1285   {
1286     EL_EMC_MAGIC_BALL,                  -1,
1287     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1288     &li.ball_active_initial,            FALSE
1289   },
1290   {
1291     EL_EMC_MAGIC_BALL,                  -1,
1292     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1293     &li.ball_content,                   EL_EMPTY, NULL,
1294     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1295   },
1296
1297   {
1298     EL_SOKOBAN_FIELD_EMPTY,             -1,
1299     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1300     &li.sb_fields_needed,               TRUE
1301   },
1302
1303   {
1304     EL_SOKOBAN_OBJECT,                  -1,
1305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1306     &li.sb_objects_needed,              TRUE
1307   },
1308
1309   {
1310     EL_MM_MCDUFFIN,                     -1,
1311     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1312     &li.mm_laser_red,                   FALSE
1313   },
1314   {
1315     EL_MM_MCDUFFIN,                     -1,
1316     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1317     &li.mm_laser_green,                 FALSE
1318   },
1319   {
1320     EL_MM_MCDUFFIN,                     -1,
1321     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1322     &li.mm_laser_blue,                  TRUE
1323   },
1324
1325   {
1326     EL_DF_LASER,                        -1,
1327     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1328     &li.df_laser_red,                   TRUE
1329   },
1330   {
1331     EL_DF_LASER,                        -1,
1332     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1333     &li.df_laser_green,                 TRUE
1334   },
1335   {
1336     EL_DF_LASER,                        -1,
1337     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1338     &li.df_laser_blue,                  FALSE
1339   },
1340
1341   {
1342     EL_MM_FUSE_ACTIVE,                  -1,
1343     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1344     &li.mm_time_fuse,                   25
1345   },
1346   {
1347     EL_MM_BOMB,                         -1,
1348     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1349     &li.mm_time_bomb,                   75
1350   },
1351
1352   {
1353     EL_MM_GRAY_BALL,                    -1,
1354     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1355     &li.mm_time_ball,                   75
1356   },
1357   {
1358     EL_MM_GRAY_BALL,                    -1,
1359     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1360     &li.mm_ball_choice_mode,            ANIM_RANDOM
1361   },
1362   {
1363     EL_MM_GRAY_BALL,                    -1,
1364     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1365     &li.mm_ball_content,                EL_EMPTY, NULL,
1366     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1367   },
1368   {
1369     EL_MM_GRAY_BALL,                    -1,
1370     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1371     &li.rotate_mm_ball_content,         TRUE
1372   },
1373   {
1374     EL_MM_GRAY_BALL,                    -1,
1375     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1376     &li.explode_mm_ball,                FALSE
1377   },
1378
1379   {
1380     EL_MM_STEEL_BLOCK,                  -1,
1381     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1382     &li.mm_time_block,                  75
1383   },
1384   {
1385     EL_MM_LIGHTBALL,                    -1,
1386     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1387     &li.score[SC_ELEM_BONUS],           10
1388   },
1389
1390   {
1391     -1,                                 -1,
1392     -1,                                 -1,
1393     NULL,                               -1
1394   }
1395 };
1396
1397 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1398 {
1399   {
1400     -1,                                 -1,
1401     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1402     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1403   },
1404   {
1405     -1,                                 -1,
1406     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1407     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1408   },
1409
1410   {
1411     -1,                                 -1,
1412     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1413     &xx_envelope.autowrap,              FALSE
1414   },
1415   {
1416     -1,                                 -1,
1417     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1418     &xx_envelope.centered,              FALSE
1419   },
1420
1421   {
1422     -1,                                 -1,
1423     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1424     &xx_envelope.text,                  -1, NULL,
1425     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1426     &xx_default_string_empty[0]
1427   },
1428
1429   {
1430     -1,                                 -1,
1431     -1,                                 -1,
1432     NULL,                               -1
1433   }
1434 };
1435
1436 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1437 {
1438   {
1439     -1,                                 -1,
1440     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1441     &xx_ei.description[0],              -1,
1442     &yy_ei.description[0],
1443     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1444     &xx_default_description[0]
1445   },
1446
1447   {
1448     -1,                                 -1,
1449     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1450     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1451     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1452   },
1453 #if ENABLE_RESERVED_CODE
1454   // (reserved for later use)
1455   {
1456     -1,                                 -1,
1457     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1458     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1459     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1460   },
1461 #endif
1462
1463   {
1464     -1,                                 -1,
1465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1466     &xx_ei.use_gfx_element,             FALSE,
1467     &yy_ei.use_gfx_element
1468   },
1469   {
1470     -1,                                 -1,
1471     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1472     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1473     &yy_ei.gfx_element_initial
1474   },
1475
1476   {
1477     -1,                                 -1,
1478     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1479     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1480     &yy_ei.access_direction
1481   },
1482
1483   {
1484     -1,                                 -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1486     &xx_ei.collect_score_initial,       10,
1487     &yy_ei.collect_score_initial
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1492     &xx_ei.collect_count_initial,       1,
1493     &yy_ei.collect_count_initial
1494   },
1495
1496   {
1497     -1,                                 -1,
1498     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1499     &xx_ei.ce_value_fixed_initial,      0,
1500     &yy_ei.ce_value_fixed_initial
1501   },
1502   {
1503     -1,                                 -1,
1504     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1505     &xx_ei.ce_value_random_initial,     0,
1506     &yy_ei.ce_value_random_initial
1507   },
1508   {
1509     -1,                                 -1,
1510     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1511     &xx_ei.use_last_ce_value,           FALSE,
1512     &yy_ei.use_last_ce_value
1513   },
1514
1515   {
1516     -1,                                 -1,
1517     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1518     &xx_ei.push_delay_fixed,            8,
1519     &yy_ei.push_delay_fixed
1520   },
1521   {
1522     -1,                                 -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1524     &xx_ei.push_delay_random,           8,
1525     &yy_ei.push_delay_random
1526   },
1527   {
1528     -1,                                 -1,
1529     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1530     &xx_ei.drop_delay_fixed,            0,
1531     &yy_ei.drop_delay_fixed
1532   },
1533   {
1534     -1,                                 -1,
1535     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1536     &xx_ei.drop_delay_random,           0,
1537     &yy_ei.drop_delay_random
1538   },
1539   {
1540     -1,                                 -1,
1541     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1542     &xx_ei.move_delay_fixed,            0,
1543     &yy_ei.move_delay_fixed
1544   },
1545   {
1546     -1,                                 -1,
1547     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1548     &xx_ei.move_delay_random,           0,
1549     &yy_ei.move_delay_random
1550   },
1551   {
1552     -1,                                 -1,
1553     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1554     &xx_ei.step_delay_fixed,            0,
1555     &yy_ei.step_delay_fixed
1556   },
1557   {
1558     -1,                                 -1,
1559     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1560     &xx_ei.step_delay_random,           0,
1561     &yy_ei.step_delay_random
1562   },
1563
1564   {
1565     -1,                                 -1,
1566     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1567     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1568     &yy_ei.move_pattern
1569   },
1570   {
1571     -1,                                 -1,
1572     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1573     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1574     &yy_ei.move_direction_initial
1575   },
1576   {
1577     -1,                                 -1,
1578     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1579     &xx_ei.move_stepsize,               TILEX / 8,
1580     &yy_ei.move_stepsize
1581   },
1582
1583   {
1584     -1,                                 -1,
1585     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1586     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1587     &yy_ei.move_enter_element
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1592     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1593     &yy_ei.move_leave_element
1594   },
1595   {
1596     -1,                                 -1,
1597     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1598     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1599     &yy_ei.move_leave_type
1600   },
1601
1602   {
1603     -1,                                 -1,
1604     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1605     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1606     &yy_ei.slippery_type
1607   },
1608
1609   {
1610     -1,                                 -1,
1611     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1612     &xx_ei.explosion_type,              EXPLODES_3X3,
1613     &yy_ei.explosion_type
1614   },
1615   {
1616     -1,                                 -1,
1617     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1618     &xx_ei.explosion_delay,             16,
1619     &yy_ei.explosion_delay
1620   },
1621   {
1622     -1,                                 -1,
1623     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1624     &xx_ei.ignition_delay,              8,
1625     &yy_ei.ignition_delay
1626   },
1627
1628   {
1629     -1,                                 -1,
1630     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1631     &xx_ei.content,                     EL_EMPTY_SPACE,
1632     &yy_ei.content,
1633     &xx_num_contents,                   1, 1
1634   },
1635
1636   // ---------- "num_change_pages" must be the last entry ---------------------
1637
1638   {
1639     -1,                                 SAVE_CONF_ALWAYS,
1640     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1641     &xx_ei.num_change_pages,            1,
1642     &yy_ei.num_change_pages
1643   },
1644
1645   {
1646     -1,                                 -1,
1647     -1,                                 -1,
1648     NULL,                               -1,
1649     NULL
1650   }
1651 };
1652
1653 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1654 {
1655   // ---------- "current_change_page" must be the first entry -----------------
1656
1657   {
1658     -1,                                 SAVE_CONF_ALWAYS,
1659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1660     &xx_current_change_page,            -1
1661   },
1662
1663   // ---------- (the remaining entries can be in any order) -------------------
1664
1665   {
1666     -1,                                 -1,
1667     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1668     &xx_change.can_change,              FALSE
1669   },
1670
1671   {
1672     -1,                                 -1,
1673     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1674     &xx_event_bits[0],                  0
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1679     &xx_event_bits[1],                  0
1680   },
1681
1682   {
1683     -1,                                 -1,
1684     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1685     &xx_change.trigger_player,          CH_PLAYER_ANY
1686   },
1687   {
1688     -1,                                 -1,
1689     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1690     &xx_change.trigger_side,            CH_SIDE_ANY
1691   },
1692   {
1693     -1,                                 -1,
1694     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1695     &xx_change.trigger_page,            CH_PAGE_ANY
1696   },
1697
1698   {
1699     -1,                                 -1,
1700     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1701     &xx_change.target_element,          EL_EMPTY_SPACE
1702   },
1703
1704   {
1705     -1,                                 -1,
1706     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1707     &xx_change.delay_fixed,             0
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1712     &xx_change.delay_random,            0
1713   },
1714   {
1715     -1,                                 -1,
1716     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1717     &xx_change.delay_frames,            FRAMES_PER_SECOND
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1723     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1724   },
1725
1726   {
1727     -1,                                 -1,
1728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1729     &xx_change.explode,                 FALSE
1730   },
1731   {
1732     -1,                                 -1,
1733     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1734     &xx_change.use_target_content,      FALSE
1735   },
1736   {
1737     -1,                                 -1,
1738     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1739     &xx_change.only_if_complete,        FALSE
1740   },
1741   {
1742     -1,                                 -1,
1743     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1744     &xx_change.use_random_replace,      FALSE
1745   },
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1749     &xx_change.random_percentage,       100
1750   },
1751   {
1752     -1,                                 -1,
1753     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1754     &xx_change.replace_when,            CP_WHEN_EMPTY
1755   },
1756
1757   {
1758     -1,                                 -1,
1759     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1760     &xx_change.has_action,              FALSE
1761   },
1762   {
1763     -1,                                 -1,
1764     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1765     &xx_change.action_type,             CA_NO_ACTION
1766   },
1767   {
1768     -1,                                 -1,
1769     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1770     &xx_change.action_mode,             CA_MODE_UNDEFINED
1771   },
1772   {
1773     -1,                                 -1,
1774     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1775     &xx_change.action_arg,              CA_ARG_UNDEFINED
1776   },
1777
1778   {
1779     -1,                                 -1,
1780     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1781     &xx_change.action_element,          EL_EMPTY_SPACE
1782   },
1783
1784   {
1785     -1,                                 -1,
1786     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1787     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1788     &xx_num_contents,                   1, 1
1789   },
1790
1791   {
1792     -1,                                 -1,
1793     -1,                                 -1,
1794     NULL,                               -1
1795   }
1796 };
1797
1798 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1799 {
1800   {
1801     -1,                                 -1,
1802     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1803     &xx_ei.description[0],              -1, NULL,
1804     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1805     &xx_default_description[0]
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1811     &xx_ei.use_gfx_element,             FALSE
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1816     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1822     &xx_group.choice_mode,              ANIM_RANDOM
1823   },
1824
1825   {
1826     -1,                                 -1,
1827     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1828     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1829     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1830   },
1831
1832   {
1833     -1,                                 -1,
1834     -1,                                 -1,
1835     NULL,                               -1
1836   }
1837 };
1838
1839 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1840 {
1841   {
1842     -1,                                 -1,
1843     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1844     &xx_ei.use_gfx_element,             FALSE
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1849     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1850   },
1851
1852   {
1853     -1,                                 -1,
1854     -1,                                 -1,
1855     NULL,                               -1
1856   }
1857 };
1858
1859 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1860 {
1861   {
1862     EL_PLAYER_1,                        -1,
1863     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1864     &li.block_snap_field,               TRUE
1865   },
1866   {
1867     EL_PLAYER_1,                        -1,
1868     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1869     &li.continuous_snapping,            TRUE
1870   },
1871   {
1872     EL_PLAYER_1,                        -1,
1873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1874     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1875   },
1876   {
1877     EL_PLAYER_1,                        -1,
1878     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1879     &li.use_start_element[0],           FALSE
1880   },
1881   {
1882     EL_PLAYER_1,                        -1,
1883     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1884     &li.start_element[0],               EL_PLAYER_1
1885   },
1886   {
1887     EL_PLAYER_1,                        -1,
1888     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1889     &li.use_artwork_element[0],         FALSE
1890   },
1891   {
1892     EL_PLAYER_1,                        -1,
1893     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1894     &li.artwork_element[0],             EL_PLAYER_1
1895   },
1896   {
1897     EL_PLAYER_1,                        -1,
1898     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1899     &li.use_explosion_element[0],       FALSE
1900   },
1901   {
1902     EL_PLAYER_1,                        -1,
1903     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1904     &li.explosion_element[0],           EL_PLAYER_1
1905   },
1906
1907   {
1908     -1,                                 -1,
1909     -1,                                 -1,
1910     NULL,                               -1
1911   }
1912 };
1913
1914 static struct
1915 {
1916   int filetype;
1917   char *id;
1918 }
1919 filetype_id_list[] =
1920 {
1921   { LEVEL_FILE_TYPE_RND,        "RND"   },
1922   { LEVEL_FILE_TYPE_BD,         "BD"    },
1923   { LEVEL_FILE_TYPE_EM,         "EM"    },
1924   { LEVEL_FILE_TYPE_SP,         "SP"    },
1925   { LEVEL_FILE_TYPE_DX,         "DX"    },
1926   { LEVEL_FILE_TYPE_SB,         "SB"    },
1927   { LEVEL_FILE_TYPE_DC,         "DC"    },
1928   { LEVEL_FILE_TYPE_MM,         "MM"    },
1929   { LEVEL_FILE_TYPE_MM,         "DF"    },
1930   { -1,                         NULL    },
1931 };
1932
1933
1934 // ============================================================================
1935 // level file functions
1936 // ============================================================================
1937
1938 static boolean check_special_flags(char *flag)
1939 {
1940   if (strEqual(options.special_flags, flag) ||
1941       strEqual(leveldir_current->special_flags, flag))
1942     return TRUE;
1943
1944   return FALSE;
1945 }
1946
1947 static struct DateInfo getCurrentDate(void)
1948 {
1949   time_t epoch_seconds = time(NULL);
1950   struct tm *now = localtime(&epoch_seconds);
1951   struct DateInfo date;
1952
1953   date.year  = now->tm_year + 1900;
1954   date.month = now->tm_mon  + 1;
1955   date.day   = now->tm_mday;
1956
1957   date.src   = DATE_SRC_CLOCK;
1958
1959   return date;
1960 }
1961
1962 static void resetEventFlags(struct ElementChangeInfo *change)
1963 {
1964   int i;
1965
1966   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1967     change->has_event[i] = FALSE;
1968 }
1969
1970 static void resetEventBits(void)
1971 {
1972   int i;
1973
1974   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1975     xx_event_bits[i] = 0;
1976 }
1977
1978 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1979 {
1980   int i;
1981
1982   /* important: only change event flag if corresponding event bit is set
1983      (this is because all xx_event_bits[] values are loaded separately,
1984      and all xx_event_bits[] values are set back to zero before loading
1985      another value xx_event_bits[x] (each value representing 32 flags)) */
1986
1987   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1988     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1989       change->has_event[i] = TRUE;
1990 }
1991
1992 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1993 {
1994   int i;
1995
1996   /* in contrast to the above function setEventFlagsFromEventBits(), it
1997      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1998      depending on the corresponding change->has_event[i] values here, as
1999      all xx_event_bits[] values are reset in resetEventBits() before */
2000
2001   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2002     if (change->has_event[i])
2003       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2004 }
2005
2006 static char *getDefaultElementDescription(struct ElementInfo *ei)
2007 {
2008   static char description[MAX_ELEMENT_NAME_LEN + 1];
2009   char *default_description = (ei->custom_description != NULL ?
2010                                ei->custom_description :
2011                                ei->editor_description);
2012   int i;
2013
2014   // always start with reliable default values
2015   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2016     description[i] = '\0';
2017
2018   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2019   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2020
2021   return &description[0];
2022 }
2023
2024 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2025 {
2026   char *default_description = getDefaultElementDescription(ei);
2027   int i;
2028
2029   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2030     ei->description[i] = default_description[i];
2031 }
2032
2033 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2034 {
2035   int i;
2036
2037   for (i = 0; conf[i].data_type != -1; i++)
2038   {
2039     int default_value = conf[i].default_value;
2040     int data_type = conf[i].data_type;
2041     int conf_type = conf[i].conf_type;
2042     int byte_mask = conf_type & CONF_MASK_BYTES;
2043
2044     if (byte_mask == CONF_MASK_MULTI_BYTES)
2045     {
2046       int default_num_entities = conf[i].default_num_entities;
2047       int max_num_entities = conf[i].max_num_entities;
2048
2049       *(int *)(conf[i].num_entities) = default_num_entities;
2050
2051       if (data_type == TYPE_STRING)
2052       {
2053         char *default_string = conf[i].default_string;
2054         char *string = (char *)(conf[i].value);
2055
2056         strncpy(string, default_string, max_num_entities);
2057       }
2058       else if (data_type == TYPE_ELEMENT_LIST)
2059       {
2060         int *element_array = (int *)(conf[i].value);
2061         int j;
2062
2063         for (j = 0; j < max_num_entities; j++)
2064           element_array[j] = default_value;
2065       }
2066       else if (data_type == TYPE_CONTENT_LIST)
2067       {
2068         struct Content *content = (struct Content *)(conf[i].value);
2069         int c, x, y;
2070
2071         for (c = 0; c < max_num_entities; c++)
2072           for (y = 0; y < 3; y++)
2073             for (x = 0; x < 3; x++)
2074               content[c].e[x][y] = default_value;
2075       }
2076     }
2077     else        // constant size configuration data (1, 2 or 4 bytes)
2078     {
2079       if (data_type == TYPE_BOOLEAN)
2080         *(boolean *)(conf[i].value) = default_value;
2081       else
2082         *(int *)    (conf[i].value) = default_value;
2083     }
2084   }
2085 }
2086
2087 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2088 {
2089   int i;
2090
2091   for (i = 0; conf[i].data_type != -1; i++)
2092   {
2093     int data_type = conf[i].data_type;
2094     int conf_type = conf[i].conf_type;
2095     int byte_mask = conf_type & CONF_MASK_BYTES;
2096
2097     if (byte_mask == CONF_MASK_MULTI_BYTES)
2098     {
2099       int max_num_entities = conf[i].max_num_entities;
2100
2101       if (data_type == TYPE_STRING)
2102       {
2103         char *string      = (char *)(conf[i].value);
2104         char *string_copy = (char *)(conf[i].value_copy);
2105
2106         strncpy(string_copy, string, max_num_entities);
2107       }
2108       else if (data_type == TYPE_ELEMENT_LIST)
2109       {
2110         int *element_array      = (int *)(conf[i].value);
2111         int *element_array_copy = (int *)(conf[i].value_copy);
2112         int j;
2113
2114         for (j = 0; j < max_num_entities; j++)
2115           element_array_copy[j] = element_array[j];
2116       }
2117       else if (data_type == TYPE_CONTENT_LIST)
2118       {
2119         struct Content *content      = (struct Content *)(conf[i].value);
2120         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2121         int c, x, y;
2122
2123         for (c = 0; c < max_num_entities; c++)
2124           for (y = 0; y < 3; y++)
2125             for (x = 0; x < 3; x++)
2126               content_copy[c].e[x][y] = content[c].e[x][y];
2127       }
2128     }
2129     else        // constant size configuration data (1, 2 or 4 bytes)
2130     {
2131       if (data_type == TYPE_BOOLEAN)
2132         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2133       else
2134         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2135     }
2136   }
2137 }
2138
2139 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2140 {
2141   int i;
2142
2143   xx_ei = *ei_from;     // copy element data into temporary buffer
2144   yy_ei = *ei_to;       // copy element data into temporary buffer
2145
2146   copyConfigFromConfigList(chunk_config_CUSX_base);
2147
2148   *ei_from = xx_ei;
2149   *ei_to   = yy_ei;
2150
2151   // ---------- reinitialize and copy change pages ----------
2152
2153   ei_to->num_change_pages = ei_from->num_change_pages;
2154   ei_to->current_change_page = ei_from->current_change_page;
2155
2156   setElementChangePages(ei_to, ei_to->num_change_pages);
2157
2158   for (i = 0; i < ei_to->num_change_pages; i++)
2159     ei_to->change_page[i] = ei_from->change_page[i];
2160
2161   // ---------- copy group element info ----------
2162   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2163     *ei_to->group = *ei_from->group;
2164
2165   // mark this custom element as modified
2166   ei_to->modified_settings = TRUE;
2167 }
2168
2169 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2170 {
2171   int change_page_size = sizeof(struct ElementChangeInfo);
2172
2173   ei->num_change_pages = MAX(1, change_pages);
2174
2175   ei->change_page =
2176     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2177
2178   if (ei->current_change_page >= ei->num_change_pages)
2179     ei->current_change_page = ei->num_change_pages - 1;
2180
2181   ei->change = &ei->change_page[ei->current_change_page];
2182 }
2183
2184 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2185 {
2186   xx_change = *change;          // copy change data into temporary buffer
2187
2188   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2189
2190   *change = xx_change;
2191
2192   resetEventFlags(change);
2193
2194   change->direct_action = 0;
2195   change->other_action = 0;
2196
2197   change->pre_change_function = NULL;
2198   change->change_function = NULL;
2199   change->post_change_function = NULL;
2200 }
2201
2202 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2203 {
2204   int i, x, y;
2205
2206   li = *level;          // copy level data into temporary buffer
2207   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2208   *level = li;          // copy temporary buffer back to level data
2209
2210   setLevelInfoToDefaults_BD();
2211   setLevelInfoToDefaults_EM();
2212   setLevelInfoToDefaults_SP();
2213   setLevelInfoToDefaults_MM();
2214
2215   level->native_bd_level = &native_bd_level;
2216   level->native_em_level = &native_em_level;
2217   level->native_sp_level = &native_sp_level;
2218   level->native_mm_level = &native_mm_level;
2219
2220   level->file_version = FILE_VERSION_ACTUAL;
2221   level->game_version = GAME_VERSION_ACTUAL;
2222
2223   level->creation_date = getCurrentDate();
2224
2225   level->encoding_16bit_field  = TRUE;
2226   level->encoding_16bit_yamyam = TRUE;
2227   level->encoding_16bit_amoeba = TRUE;
2228
2229   // clear level name and level author string buffers
2230   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2231     level->name[i] = '\0';
2232   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2233     level->author[i] = '\0';
2234
2235   // set level name and level author to default values
2236   strcpy(level->name, NAMELESS_LEVEL_NAME);
2237   strcpy(level->author, ANONYMOUS_NAME);
2238
2239   // set level playfield to playable default level with player and exit
2240   for (x = 0; x < MAX_LEV_FIELDX; x++)
2241     for (y = 0; y < MAX_LEV_FIELDY; y++)
2242       level->field[x][y] = EL_SAND;
2243
2244   level->field[0][0] = EL_PLAYER_1;
2245   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2246
2247   BorderElement = EL_STEELWALL;
2248
2249   // detect custom elements when loading them
2250   level->file_has_custom_elements = FALSE;
2251
2252   // set all bug compatibility flags to "false" => do not emulate this bug
2253   level->use_action_after_change_bug = FALSE;
2254
2255   if (leveldir_current)
2256   {
2257     // try to determine better author name than 'anonymous'
2258     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2259     {
2260       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2261       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2262     }
2263     else
2264     {
2265       switch (LEVELCLASS(leveldir_current))
2266       {
2267         case LEVELCLASS_TUTORIAL:
2268           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2269           break;
2270
2271         case LEVELCLASS_CONTRIB:
2272           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2273           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2274           break;
2275
2276         case LEVELCLASS_PRIVATE:
2277           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2278           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2279           break;
2280
2281         default:
2282           // keep default value
2283           break;
2284       }
2285     }
2286   }
2287 }
2288
2289 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2290 {
2291   static boolean clipboard_elements_initialized = FALSE;
2292   int i;
2293
2294   InitElementPropertiesStatic();
2295
2296   li = *level;          // copy level data into temporary buffer
2297   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2298   *level = li;          // copy temporary buffer back to level data
2299
2300   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2301   {
2302     int element = i;
2303     struct ElementInfo *ei = &element_info[element];
2304
2305     if (element == EL_MM_GRAY_BALL)
2306     {
2307       struct LevelInfo_MM *level_mm = level->native_mm_level;
2308       int j;
2309
2310       for (j = 0; j < level->num_mm_ball_contents; j++)
2311         level->mm_ball_content[j] =
2312           map_element_MM_to_RND(level_mm->ball_content[j]);
2313     }
2314
2315     // never initialize clipboard elements after the very first time
2316     // (to be able to use clipboard elements between several levels)
2317     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2318       continue;
2319
2320     if (IS_ENVELOPE(element))
2321     {
2322       int envelope_nr = element - EL_ENVELOPE_1;
2323
2324       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2325
2326       level->envelope[envelope_nr] = xx_envelope;
2327     }
2328
2329     if (IS_CUSTOM_ELEMENT(element) ||
2330         IS_GROUP_ELEMENT(element) ||
2331         IS_INTERNAL_ELEMENT(element))
2332     {
2333       xx_ei = *ei;      // copy element data into temporary buffer
2334
2335       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2336
2337       *ei = xx_ei;
2338     }
2339
2340     setElementChangePages(ei, 1);
2341     setElementChangeInfoToDefaults(ei->change);
2342
2343     if (IS_CUSTOM_ELEMENT(element) ||
2344         IS_GROUP_ELEMENT(element))
2345     {
2346       setElementDescriptionToDefault(ei);
2347
2348       ei->modified_settings = FALSE;
2349     }
2350
2351     if (IS_CUSTOM_ELEMENT(element) ||
2352         IS_INTERNAL_ELEMENT(element))
2353     {
2354       // internal values used in level editor
2355
2356       ei->access_type = 0;
2357       ei->access_layer = 0;
2358       ei->access_protected = 0;
2359       ei->walk_to_action = 0;
2360       ei->smash_targets = 0;
2361       ei->deadliness = 0;
2362
2363       ei->can_explode_by_fire = FALSE;
2364       ei->can_explode_smashed = FALSE;
2365       ei->can_explode_impact = FALSE;
2366
2367       ei->current_change_page = 0;
2368     }
2369
2370     if (IS_GROUP_ELEMENT(element) ||
2371         IS_INTERNAL_ELEMENT(element))
2372     {
2373       struct ElementGroupInfo *group;
2374
2375       // initialize memory for list of elements in group
2376       if (ei->group == NULL)
2377         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2378
2379       group = ei->group;
2380
2381       xx_group = *group;        // copy group data into temporary buffer
2382
2383       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2384
2385       *group = xx_group;
2386     }
2387
2388     if (IS_EMPTY_ELEMENT(element) ||
2389         IS_INTERNAL_ELEMENT(element))
2390     {
2391       xx_ei = *ei;              // copy element data into temporary buffer
2392
2393       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2394
2395       *ei = xx_ei;
2396     }
2397   }
2398
2399   clipboard_elements_initialized = TRUE;
2400 }
2401
2402 static void setLevelInfoToDefaults(struct LevelInfo *level,
2403                                    boolean level_info_only,
2404                                    boolean reset_file_status)
2405 {
2406   setLevelInfoToDefaults_Level(level);
2407
2408   if (!level_info_only)
2409     setLevelInfoToDefaults_Elements(level);
2410
2411   if (reset_file_status)
2412   {
2413     level->no_valid_file = FALSE;
2414     level->no_level_file = FALSE;
2415   }
2416
2417   level->changed = FALSE;
2418 }
2419
2420 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2421 {
2422   level_file_info->nr = 0;
2423   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2424   level_file_info->packed = FALSE;
2425
2426   setString(&level_file_info->basename, NULL);
2427   setString(&level_file_info->filename, NULL);
2428 }
2429
2430 int getMappedElement_SB(int, boolean);
2431
2432 static void ActivateLevelTemplate(void)
2433 {
2434   int x, y;
2435
2436   if (check_special_flags("load_xsb_to_ces"))
2437   {
2438     // fill smaller playfields with padding "beyond border wall" elements
2439     if (level.fieldx < level_template.fieldx ||
2440         level.fieldy < level_template.fieldy)
2441     {
2442       short field[level.fieldx][level.fieldy];
2443       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2444       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2445       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2446       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2447
2448       // copy old playfield (which is smaller than the visible area)
2449       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2450         field[x][y] = level.field[x][y];
2451
2452       // fill new, larger playfield with "beyond border wall" elements
2453       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2454         level.field[x][y] = getMappedElement_SB('_', TRUE);
2455
2456       // copy the old playfield to the middle of the new playfield
2457       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2458         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2459
2460       level.fieldx = new_fieldx;
2461       level.fieldy = new_fieldy;
2462     }
2463   }
2464
2465   // Currently there is no special action needed to activate the template
2466   // data, because 'element_info' property settings overwrite the original
2467   // level data, while all other variables do not change.
2468
2469   // Exception: 'from_level_template' elements in the original level playfield
2470   // are overwritten with the corresponding elements at the same position in
2471   // playfield from the level template.
2472
2473   for (x = 0; x < level.fieldx; x++)
2474     for (y = 0; y < level.fieldy; y++)
2475       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2476         level.field[x][y] = level_template.field[x][y];
2477
2478   if (check_special_flags("load_xsb_to_ces"))
2479   {
2480     struct LevelInfo level_backup = level;
2481
2482     // overwrite all individual level settings from template level settings
2483     level = level_template;
2484
2485     // restore level file info
2486     level.file_info = level_backup.file_info;
2487
2488     // restore playfield size
2489     level.fieldx = level_backup.fieldx;
2490     level.fieldy = level_backup.fieldy;
2491
2492     // restore playfield content
2493     for (x = 0; x < level.fieldx; x++)
2494       for (y = 0; y < level.fieldy; y++)
2495         level.field[x][y] = level_backup.field[x][y];
2496
2497     // restore name and author from individual level
2498     strcpy(level.name,   level_backup.name);
2499     strcpy(level.author, level_backup.author);
2500
2501     // restore flag "use_custom_template"
2502     level.use_custom_template = level_backup.use_custom_template;
2503   }
2504 }
2505
2506 static boolean checkForPackageFromBasename_BD(char *basename)
2507 {
2508   // check for native BD level file extensions
2509   if (!strSuffixLower(basename, ".bd") &&
2510       !strSuffixLower(basename, ".bdr") &&
2511       !strSuffixLower(basename, ".brc") &&
2512       !strSuffixLower(basename, ".gds"))
2513     return FALSE;
2514
2515   // check for standard single-level BD files (like "001.bd")
2516   if (strSuffixLower(basename, ".bd") &&
2517       strlen(basename) == 6 &&
2518       basename[0] >= '0' && basename[0] <= '9' &&
2519       basename[1] >= '0' && basename[1] <= '9' &&
2520       basename[2] >= '0' && basename[2] <= '9')
2521     return FALSE;
2522
2523   // this is a level package in native BD file format
2524   return TRUE;
2525 }
2526
2527 static char *getLevelFilenameFromBasename(char *basename)
2528 {
2529   static char *filename = NULL;
2530
2531   checked_free(filename);
2532
2533   filename = getPath2(getCurrentLevelDir(), basename);
2534
2535   return filename;
2536 }
2537
2538 static int getFileTypeFromBasename(char *basename)
2539 {
2540   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2541
2542   static char *filename = NULL;
2543   struct stat file_status;
2544
2545   // ---------- try to determine file type from filename ----------
2546
2547   // check for typical filename of a Supaplex level package file
2548   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2549     return LEVEL_FILE_TYPE_SP;
2550
2551   // check for typical filename of a Diamond Caves II level package file
2552   if (strSuffixLower(basename, ".dc") ||
2553       strSuffixLower(basename, ".dc2"))
2554     return LEVEL_FILE_TYPE_DC;
2555
2556   // check for typical filename of a Sokoban level package file
2557   if (strSuffixLower(basename, ".xsb") &&
2558       strchr(basename, '%') == NULL)
2559     return LEVEL_FILE_TYPE_SB;
2560
2561   // check for typical filename of a Boulder Dash (GDash) level package file
2562   if (checkForPackageFromBasename_BD(basename))
2563     return LEVEL_FILE_TYPE_BD;
2564
2565   // ---------- try to determine file type from filesize ----------
2566
2567   checked_free(filename);
2568   filename = getPath2(getCurrentLevelDir(), basename);
2569
2570   if (stat(filename, &file_status) == 0)
2571   {
2572     // check for typical filesize of a Supaplex level package file
2573     if (file_status.st_size == 170496)
2574       return LEVEL_FILE_TYPE_SP;
2575   }
2576
2577   return LEVEL_FILE_TYPE_UNKNOWN;
2578 }
2579
2580 static int getFileTypeFromMagicBytes(char *filename, int type)
2581 {
2582   File *file;
2583
2584   if ((file = openFile(filename, MODE_READ)))
2585   {
2586     char chunk_name[CHUNK_ID_LEN + 1];
2587
2588     getFileChunkBE(file, chunk_name, NULL);
2589
2590     if (strEqual(chunk_name, "MMII") ||
2591         strEqual(chunk_name, "MIRR"))
2592       type = LEVEL_FILE_TYPE_MM;
2593
2594     closeFile(file);
2595   }
2596
2597   return type;
2598 }
2599
2600 static boolean checkForPackageFromBasename(char *basename)
2601 {
2602   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2603   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2604
2605   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2606 }
2607
2608 static char *getSingleLevelBasenameExt(int nr, char *extension)
2609 {
2610   static char basename[MAX_FILENAME_LEN];
2611
2612   if (nr < 0)
2613     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2614   else
2615     sprintf(basename, "%03d.%s", nr, extension);
2616
2617   return basename;
2618 }
2619
2620 static char *getSingleLevelBasename(int nr)
2621 {
2622   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2623 }
2624
2625 static char *getPackedLevelBasename(int type)
2626 {
2627   static char basename[MAX_FILENAME_LEN];
2628   char *directory = getCurrentLevelDir();
2629   Directory *dir;
2630   DirectoryEntry *dir_entry;
2631
2632   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2633
2634   if ((dir = openDirectory(directory)) == NULL)
2635   {
2636     Warn("cannot read current level directory '%s'", directory);
2637
2638     return basename;
2639   }
2640
2641   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2642   {
2643     char *entry_basename = dir_entry->basename;
2644     int entry_type = getFileTypeFromBasename(entry_basename);
2645
2646     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2647     {
2648       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2649           type == entry_type)
2650       {
2651         strcpy(basename, entry_basename);
2652
2653         break;
2654       }
2655     }
2656   }
2657
2658   closeDirectory(dir);
2659
2660   return basename;
2661 }
2662
2663 static char *getSingleLevelFilename(int nr)
2664 {
2665   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2666 }
2667
2668 #if ENABLE_UNUSED_CODE
2669 static char *getPackedLevelFilename(int type)
2670 {
2671   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2672 }
2673 #endif
2674
2675 char *getDefaultLevelFilename(int nr)
2676 {
2677   return getSingleLevelFilename(nr);
2678 }
2679
2680 #if ENABLE_UNUSED_CODE
2681 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2682                                                  int type)
2683 {
2684   lfi->type = type;
2685   lfi->packed = FALSE;
2686
2687   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2688   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2689 }
2690 #endif
2691
2692 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2693                                                  int type, char *format, ...)
2694 {
2695   static char basename[MAX_FILENAME_LEN];
2696   va_list ap;
2697
2698   va_start(ap, format);
2699   vsprintf(basename, format, ap);
2700   va_end(ap);
2701
2702   lfi->type = type;
2703   lfi->packed = FALSE;
2704
2705   setString(&lfi->basename, basename);
2706   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2707 }
2708
2709 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2710                                                  int type)
2711 {
2712   lfi->type = type;
2713   lfi->packed = TRUE;
2714
2715   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2716   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2717 }
2718
2719 static int getFiletypeFromID(char *filetype_id)
2720 {
2721   char *filetype_id_lower;
2722   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2723   int i;
2724
2725   if (filetype_id == NULL)
2726     return LEVEL_FILE_TYPE_UNKNOWN;
2727
2728   filetype_id_lower = getStringToLower(filetype_id);
2729
2730   for (i = 0; filetype_id_list[i].id != NULL; i++)
2731   {
2732     char *id_lower = getStringToLower(filetype_id_list[i].id);
2733     
2734     if (strEqual(filetype_id_lower, id_lower))
2735       filetype = filetype_id_list[i].filetype;
2736
2737     free(id_lower);
2738
2739     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2740       break;
2741   }
2742
2743   free(filetype_id_lower);
2744
2745   return filetype;
2746 }
2747
2748 char *getLocalLevelTemplateFilename(void)
2749 {
2750   return getDefaultLevelFilename(-1);
2751 }
2752
2753 char *getGlobalLevelTemplateFilename(void)
2754 {
2755   // global variable "leveldir_current" must be modified in the loop below
2756   LevelDirTree *leveldir_current_last = leveldir_current;
2757   char *filename = NULL;
2758
2759   // check for template level in path from current to topmost tree node
2760
2761   while (leveldir_current != NULL)
2762   {
2763     filename = getDefaultLevelFilename(-1);
2764
2765     if (fileExists(filename))
2766       break;
2767
2768     leveldir_current = leveldir_current->node_parent;
2769   }
2770
2771   // restore global variable "leveldir_current" modified in above loop
2772   leveldir_current = leveldir_current_last;
2773
2774   return filename;
2775 }
2776
2777 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2778 {
2779   int nr = lfi->nr;
2780
2781   // special case: level number is negative => check for level template file
2782   if (nr < 0)
2783   {
2784     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2785                                          getSingleLevelBasename(-1));
2786
2787     // replace local level template filename with global template filename
2788     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2789
2790     // no fallback if template file not existing
2791     return;
2792   }
2793
2794   // special case: check for file name/pattern specified in "levelinfo.conf"
2795   if (leveldir_current->level_filename != NULL)
2796   {
2797     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2798
2799     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2800                                          leveldir_current->level_filename, nr);
2801
2802     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2803
2804     if (fileExists(lfi->filename))
2805       return;
2806   }
2807   else if (leveldir_current->level_filetype != NULL)
2808   {
2809     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2810
2811     // check for specified native level file with standard file name
2812     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2813                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2814     if (fileExists(lfi->filename))
2815       return;
2816   }
2817
2818   // check for native Rocks'n'Diamonds level file
2819   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2820                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2821   if (fileExists(lfi->filename))
2822     return;
2823
2824   // check for native Boulder Dash level file
2825   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2826   if (fileExists(lfi->filename))
2827     return;
2828
2829   // check for Emerald Mine level file (V1)
2830   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2831                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2832   if (fileExists(lfi->filename))
2833     return;
2834   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2835                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2836   if (fileExists(lfi->filename))
2837     return;
2838
2839   // check for Emerald Mine level file (V2 to V5)
2840   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2841   if (fileExists(lfi->filename))
2842     return;
2843
2844   // check for Emerald Mine level file (V6 / single mode)
2845   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2846   if (fileExists(lfi->filename))
2847     return;
2848   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2849   if (fileExists(lfi->filename))
2850     return;
2851
2852   // check for Emerald Mine level file (V6 / teamwork mode)
2853   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2854   if (fileExists(lfi->filename))
2855     return;
2856   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2857   if (fileExists(lfi->filename))
2858     return;
2859
2860   // check for various packed level file formats
2861   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2862   if (fileExists(lfi->filename))
2863     return;
2864
2865   // no known level file found -- use default values (and fail later)
2866   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2867                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2868 }
2869
2870 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2871 {
2872   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2873     lfi->type = getFileTypeFromBasename(lfi->basename);
2874
2875   if (lfi->type == LEVEL_FILE_TYPE_RND)
2876     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2877 }
2878
2879 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2880 {
2881   // always start with reliable default values
2882   setFileInfoToDefaults(level_file_info);
2883
2884   level_file_info->nr = nr;     // set requested level number
2885
2886   determineLevelFileInfo_Filename(level_file_info);
2887   determineLevelFileInfo_Filetype(level_file_info);
2888 }
2889
2890 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2891                               struct LevelFileInfo *lfi_to)
2892 {
2893   lfi_to->nr     = lfi_from->nr;
2894   lfi_to->type   = lfi_from->type;
2895   lfi_to->packed = lfi_from->packed;
2896
2897   setString(&lfi_to->basename, lfi_from->basename);
2898   setString(&lfi_to->filename, lfi_from->filename);
2899 }
2900
2901 // ----------------------------------------------------------------------------
2902 // functions for loading R'n'D level
2903 // ----------------------------------------------------------------------------
2904
2905 int getMappedElement(int element)
2906 {
2907   // remap some (historic, now obsolete) elements
2908
2909   switch (element)
2910   {
2911     case EL_PLAYER_OBSOLETE:
2912       element = EL_PLAYER_1;
2913       break;
2914
2915     case EL_KEY_OBSOLETE:
2916       element = EL_KEY_1;
2917       break;
2918
2919     case EL_EM_KEY_1_FILE_OBSOLETE:
2920       element = EL_EM_KEY_1;
2921       break;
2922
2923     case EL_EM_KEY_2_FILE_OBSOLETE:
2924       element = EL_EM_KEY_2;
2925       break;
2926
2927     case EL_EM_KEY_3_FILE_OBSOLETE:
2928       element = EL_EM_KEY_3;
2929       break;
2930
2931     case EL_EM_KEY_4_FILE_OBSOLETE:
2932       element = EL_EM_KEY_4;
2933       break;
2934
2935     case EL_ENVELOPE_OBSOLETE:
2936       element = EL_ENVELOPE_1;
2937       break;
2938
2939     case EL_SP_EMPTY:
2940       element = EL_EMPTY;
2941       break;
2942
2943     default:
2944       if (element >= NUM_FILE_ELEMENTS)
2945       {
2946         Warn("invalid level element %d", element);
2947
2948         element = EL_UNKNOWN;
2949       }
2950       break;
2951   }
2952
2953   return element;
2954 }
2955
2956 static int getMappedElementByVersion(int element, int game_version)
2957 {
2958   // remap some elements due to certain game version
2959
2960   if (game_version <= VERSION_IDENT(2,2,0,0))
2961   {
2962     // map game font elements
2963     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2964                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2965                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2966                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2967   }
2968
2969   if (game_version < VERSION_IDENT(3,0,0,0))
2970   {
2971     // map Supaplex gravity tube elements
2972     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2973                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2974                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2975                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2976                element);
2977   }
2978
2979   return element;
2980 }
2981
2982 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2983 {
2984   level->file_version = getFileVersion(file);
2985   level->game_version = getFileVersion(file);
2986
2987   return chunk_size;
2988 }
2989
2990 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2991 {
2992   level->creation_date.year  = getFile16BitBE(file);
2993   level->creation_date.month = getFile8Bit(file);
2994   level->creation_date.day   = getFile8Bit(file);
2995
2996   level->creation_date.src   = DATE_SRC_LEVELFILE;
2997
2998   return chunk_size;
2999 }
3000
3001 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3002 {
3003   int initial_player_stepsize;
3004   int initial_player_gravity;
3005   int i, x, y;
3006
3007   level->fieldx = getFile8Bit(file);
3008   level->fieldy = getFile8Bit(file);
3009
3010   level->time           = getFile16BitBE(file);
3011   level->gems_needed    = getFile16BitBE(file);
3012
3013   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3014     level->name[i] = getFile8Bit(file);
3015   level->name[MAX_LEVEL_NAME_LEN] = 0;
3016
3017   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3018     level->score[i] = getFile8Bit(file);
3019
3020   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3021   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3022     for (y = 0; y < 3; y++)
3023       for (x = 0; x < 3; x++)
3024         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3025
3026   level->amoeba_speed           = getFile8Bit(file);
3027   level->time_magic_wall        = getFile8Bit(file);
3028   level->time_wheel             = getFile8Bit(file);
3029   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3030
3031   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3032                                    STEPSIZE_NORMAL);
3033
3034   for (i = 0; i < MAX_PLAYERS; i++)
3035     level->initial_player_stepsize[i] = initial_player_stepsize;
3036
3037   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3038
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     level->initial_player_gravity[i] = initial_player_gravity;
3041
3042   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3043   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3044
3045   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3046
3047   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3048   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3049   level->can_move_into_acid_bits = getFile32BitBE(file);
3050   level->dont_collide_with_bits = getFile8Bit(file);
3051
3052   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3053   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3054
3055   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3056   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3057   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3058
3059   level->game_engine_type       = getFile8Bit(file);
3060
3061   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3062
3063   return chunk_size;
3064 }
3065
3066 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3067 {
3068   int i;
3069
3070   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3071     level->name[i] = getFile8Bit(file);
3072   level->name[MAX_LEVEL_NAME_LEN] = 0;
3073
3074   return chunk_size;
3075 }
3076
3077 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3078 {
3079   int i;
3080
3081   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3082     level->author[i] = getFile8Bit(file);
3083   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3084
3085   return chunk_size;
3086 }
3087
3088 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3089 {
3090   int x, y;
3091   int chunk_size_expected = level->fieldx * level->fieldy;
3092
3093   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3094      stored with 16-bit encoding (and should be twice as big then).
3095      Even worse, playfield data was stored 16-bit when only yamyam content
3096      contained 16-bit elements and vice versa. */
3097
3098   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3099     chunk_size_expected *= 2;
3100
3101   if (chunk_size_expected != chunk_size)
3102   {
3103     ReadUnusedBytesFromFile(file, chunk_size);
3104     return chunk_size_expected;
3105   }
3106
3107   for (y = 0; y < level->fieldy; y++)
3108     for (x = 0; x < level->fieldx; x++)
3109       level->field[x][y] =
3110         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3111                          getFile8Bit(file));
3112   return chunk_size;
3113 }
3114
3115 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3116 {
3117   int i, x, y;
3118   int header_size = 4;
3119   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3120   int chunk_size_expected = header_size + content_size;
3121
3122   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3123      stored with 16-bit encoding (and should be twice as big then).
3124      Even worse, playfield data was stored 16-bit when only yamyam content
3125      contained 16-bit elements and vice versa. */
3126
3127   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3128     chunk_size_expected += content_size;
3129
3130   if (chunk_size_expected != chunk_size)
3131   {
3132     ReadUnusedBytesFromFile(file, chunk_size);
3133     return chunk_size_expected;
3134   }
3135
3136   getFile8Bit(file);
3137   level->num_yamyam_contents = getFile8Bit(file);
3138   getFile8Bit(file);
3139   getFile8Bit(file);
3140
3141   // correct invalid number of content fields -- should never happen
3142   if (level->num_yamyam_contents < 1 ||
3143       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3144     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3145
3146   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3147     for (y = 0; y < 3; y++)
3148       for (x = 0; x < 3; x++)
3149         level->yamyam_content[i].e[x][y] =
3150           getMappedElement(level->encoding_16bit_field ?
3151                            getFile16BitBE(file) : getFile8Bit(file));
3152   return chunk_size;
3153 }
3154
3155 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3156 {
3157   int i, x, y;
3158   int element;
3159   int num_contents;
3160   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3161
3162   element = getMappedElement(getFile16BitBE(file));
3163   num_contents = getFile8Bit(file);
3164
3165   getFile8Bit(file);    // content x size (unused)
3166   getFile8Bit(file);    // content y size (unused)
3167
3168   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3169
3170   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3171     for (y = 0; y < 3; y++)
3172       for (x = 0; x < 3; x++)
3173         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3174
3175   // correct invalid number of content fields -- should never happen
3176   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3177     num_contents = STD_ELEMENT_CONTENTS;
3178
3179   if (element == EL_YAMYAM)
3180   {
3181     level->num_yamyam_contents = num_contents;
3182
3183     for (i = 0; i < num_contents; i++)
3184       for (y = 0; y < 3; y++)
3185         for (x = 0; x < 3; x++)
3186           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3187   }
3188   else if (element == EL_BD_AMOEBA)
3189   {
3190     level->amoeba_content = content_array[0][0][0];
3191   }
3192   else
3193   {
3194     Warn("cannot load content for element '%d'", element);
3195   }
3196
3197   return chunk_size;
3198 }
3199
3200 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3201 {
3202   int i;
3203   int element;
3204   int envelope_nr;
3205   int envelope_len;
3206   int chunk_size_expected;
3207
3208   element = getMappedElement(getFile16BitBE(file));
3209   if (!IS_ENVELOPE(element))
3210     element = EL_ENVELOPE_1;
3211
3212   envelope_nr = element - EL_ENVELOPE_1;
3213
3214   envelope_len = getFile16BitBE(file);
3215
3216   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3217   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3218
3219   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3220
3221   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3222   if (chunk_size_expected != chunk_size)
3223   {
3224     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3225     return chunk_size_expected;
3226   }
3227
3228   for (i = 0; i < envelope_len; i++)
3229     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3230
3231   return chunk_size;
3232 }
3233
3234 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3235 {
3236   int num_changed_custom_elements = getFile16BitBE(file);
3237   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3238   int i;
3239
3240   if (chunk_size_expected != chunk_size)
3241   {
3242     ReadUnusedBytesFromFile(file, chunk_size - 2);
3243     return chunk_size_expected;
3244   }
3245
3246   for (i = 0; i < num_changed_custom_elements; i++)
3247   {
3248     int element = getMappedElement(getFile16BitBE(file));
3249     int properties = getFile32BitBE(file);
3250
3251     if (IS_CUSTOM_ELEMENT(element))
3252       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3253     else
3254       Warn("invalid custom element number %d", element);
3255
3256     // older game versions that wrote level files with CUS1 chunks used
3257     // different default push delay values (not yet stored in level file)
3258     element_info[element].push_delay_fixed = 2;
3259     element_info[element].push_delay_random = 8;
3260   }
3261
3262   level->file_has_custom_elements = TRUE;
3263
3264   return chunk_size;
3265 }
3266
3267 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3268 {
3269   int num_changed_custom_elements = getFile16BitBE(file);
3270   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3271   int i;
3272
3273   if (chunk_size_expected != chunk_size)
3274   {
3275     ReadUnusedBytesFromFile(file, chunk_size - 2);
3276     return chunk_size_expected;
3277   }
3278
3279   for (i = 0; i < num_changed_custom_elements; i++)
3280   {
3281     int element = getMappedElement(getFile16BitBE(file));
3282     int custom_target_element = getMappedElement(getFile16BitBE(file));
3283
3284     if (IS_CUSTOM_ELEMENT(element))
3285       element_info[element].change->target_element = custom_target_element;
3286     else
3287       Warn("invalid custom element number %d", element);
3288   }
3289
3290   level->file_has_custom_elements = TRUE;
3291
3292   return chunk_size;
3293 }
3294
3295 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3296 {
3297   int num_changed_custom_elements = getFile16BitBE(file);
3298   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3299   int i, j, x, y;
3300
3301   if (chunk_size_expected != chunk_size)
3302   {
3303     ReadUnusedBytesFromFile(file, chunk_size - 2);
3304     return chunk_size_expected;
3305   }
3306
3307   for (i = 0; i < num_changed_custom_elements; i++)
3308   {
3309     int element = getMappedElement(getFile16BitBE(file));
3310     struct ElementInfo *ei = &element_info[element];
3311     unsigned int event_bits;
3312
3313     if (!IS_CUSTOM_ELEMENT(element))
3314     {
3315       Warn("invalid custom element number %d", element);
3316
3317       element = EL_INTERNAL_DUMMY;
3318     }
3319
3320     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3321       ei->description[j] = getFile8Bit(file);
3322     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3323
3324     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3325
3326     // some free bytes for future properties and padding
3327     ReadUnusedBytesFromFile(file, 7);
3328
3329     ei->use_gfx_element = getFile8Bit(file);
3330     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3331
3332     ei->collect_score_initial = getFile8Bit(file);
3333     ei->collect_count_initial = getFile8Bit(file);
3334
3335     ei->push_delay_fixed = getFile16BitBE(file);
3336     ei->push_delay_random = getFile16BitBE(file);
3337     ei->move_delay_fixed = getFile16BitBE(file);
3338     ei->move_delay_random = getFile16BitBE(file);
3339
3340     ei->move_pattern = getFile16BitBE(file);
3341     ei->move_direction_initial = getFile8Bit(file);
3342     ei->move_stepsize = getFile8Bit(file);
3343
3344     for (y = 0; y < 3; y++)
3345       for (x = 0; x < 3; x++)
3346         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3347
3348     // bits 0 - 31 of "has_event[]"
3349     event_bits = getFile32BitBE(file);
3350     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3351       if (event_bits & (1u << j))
3352         ei->change->has_event[j] = TRUE;
3353
3354     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3355
3356     ei->change->delay_fixed = getFile16BitBE(file);
3357     ei->change->delay_random = getFile16BitBE(file);
3358     ei->change->delay_frames = getFile16BitBE(file);
3359
3360     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3361
3362     ei->change->explode = getFile8Bit(file);
3363     ei->change->use_target_content = getFile8Bit(file);
3364     ei->change->only_if_complete = getFile8Bit(file);
3365     ei->change->use_random_replace = getFile8Bit(file);
3366
3367     ei->change->random_percentage = getFile8Bit(file);
3368     ei->change->replace_when = getFile8Bit(file);
3369
3370     for (y = 0; y < 3; y++)
3371       for (x = 0; x < 3; x++)
3372         ei->change->target_content.e[x][y] =
3373           getMappedElement(getFile16BitBE(file));
3374
3375     ei->slippery_type = getFile8Bit(file);
3376
3377     // some free bytes for future properties and padding
3378     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3379
3380     // mark that this custom element has been modified
3381     ei->modified_settings = TRUE;
3382   }
3383
3384   level->file_has_custom_elements = TRUE;
3385
3386   return chunk_size;
3387 }
3388
3389 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3390 {
3391   struct ElementInfo *ei;
3392   int chunk_size_expected;
3393   int element;
3394   int i, j, x, y;
3395
3396   // ---------- custom element base property values (96 bytes) ----------------
3397
3398   element = getMappedElement(getFile16BitBE(file));
3399
3400   if (!IS_CUSTOM_ELEMENT(element))
3401   {
3402     Warn("invalid custom element number %d", element);
3403
3404     ReadUnusedBytesFromFile(file, chunk_size - 2);
3405
3406     return chunk_size;
3407   }
3408
3409   ei = &element_info[element];
3410
3411   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3412     ei->description[i] = getFile8Bit(file);
3413   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3414
3415   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3416
3417   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3418
3419   ei->num_change_pages = getFile8Bit(file);
3420
3421   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3422   if (chunk_size_expected != chunk_size)
3423   {
3424     ReadUnusedBytesFromFile(file, chunk_size - 43);
3425     return chunk_size_expected;
3426   }
3427
3428   ei->ce_value_fixed_initial = getFile16BitBE(file);
3429   ei->ce_value_random_initial = getFile16BitBE(file);
3430   ei->use_last_ce_value = getFile8Bit(file);
3431
3432   ei->use_gfx_element = getFile8Bit(file);
3433   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3434
3435   ei->collect_score_initial = getFile8Bit(file);
3436   ei->collect_count_initial = getFile8Bit(file);
3437
3438   ei->drop_delay_fixed = getFile8Bit(file);
3439   ei->push_delay_fixed = getFile8Bit(file);
3440   ei->drop_delay_random = getFile8Bit(file);
3441   ei->push_delay_random = getFile8Bit(file);
3442   ei->move_delay_fixed = getFile16BitBE(file);
3443   ei->move_delay_random = getFile16BitBE(file);
3444
3445   // bits 0 - 15 of "move_pattern" ...
3446   ei->move_pattern = getFile16BitBE(file);
3447   ei->move_direction_initial = getFile8Bit(file);
3448   ei->move_stepsize = getFile8Bit(file);
3449
3450   ei->slippery_type = getFile8Bit(file);
3451
3452   for (y = 0; y < 3; y++)
3453     for (x = 0; x < 3; x++)
3454       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3455
3456   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3457   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3458   ei->move_leave_type = getFile8Bit(file);
3459
3460   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3461   ei->move_pattern |= (getFile16BitBE(file) << 16);
3462
3463   ei->access_direction = getFile8Bit(file);
3464
3465   ei->explosion_delay = getFile8Bit(file);
3466   ei->ignition_delay = getFile8Bit(file);
3467   ei->explosion_type = getFile8Bit(file);
3468
3469   // some free bytes for future custom property values and padding
3470   ReadUnusedBytesFromFile(file, 1);
3471
3472   // ---------- change page property values (48 bytes) ------------------------
3473
3474   setElementChangePages(ei, ei->num_change_pages);
3475
3476   for (i = 0; i < ei->num_change_pages; i++)
3477   {
3478     struct ElementChangeInfo *change = &ei->change_page[i];
3479     unsigned int event_bits;
3480
3481     // always start with reliable default values
3482     setElementChangeInfoToDefaults(change);
3483
3484     // bits 0 - 31 of "has_event[]" ...
3485     event_bits = getFile32BitBE(file);
3486     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3487       if (event_bits & (1u << j))
3488         change->has_event[j] = TRUE;
3489
3490     change->target_element = getMappedElement(getFile16BitBE(file));
3491
3492     change->delay_fixed = getFile16BitBE(file);
3493     change->delay_random = getFile16BitBE(file);
3494     change->delay_frames = getFile16BitBE(file);
3495
3496     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3497
3498     change->explode = getFile8Bit(file);
3499     change->use_target_content = getFile8Bit(file);
3500     change->only_if_complete = getFile8Bit(file);
3501     change->use_random_replace = getFile8Bit(file);
3502
3503     change->random_percentage = getFile8Bit(file);
3504     change->replace_when = getFile8Bit(file);
3505
3506     for (y = 0; y < 3; y++)
3507       for (x = 0; x < 3; x++)
3508         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3509
3510     change->can_change = getFile8Bit(file);
3511
3512     change->trigger_side = getFile8Bit(file);
3513
3514     change->trigger_player = getFile8Bit(file);
3515     change->trigger_page = getFile8Bit(file);
3516
3517     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3518                             CH_PAGE_ANY : (1 << change->trigger_page));
3519
3520     change->has_action = getFile8Bit(file);
3521     change->action_type = getFile8Bit(file);
3522     change->action_mode = getFile8Bit(file);
3523     change->action_arg = getFile16BitBE(file);
3524
3525     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3526     event_bits = getFile8Bit(file);
3527     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3528       if (event_bits & (1u << (j - 32)))
3529         change->has_event[j] = TRUE;
3530   }
3531
3532   // mark this custom element as modified
3533   ei->modified_settings = TRUE;
3534
3535   level->file_has_custom_elements = TRUE;
3536
3537   return chunk_size;
3538 }
3539
3540 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3541 {
3542   struct ElementInfo *ei;
3543   struct ElementGroupInfo *group;
3544   int element;
3545   int i;
3546
3547   element = getMappedElement(getFile16BitBE(file));
3548
3549   if (!IS_GROUP_ELEMENT(element))
3550   {
3551     Warn("invalid group element number %d", element);
3552
3553     ReadUnusedBytesFromFile(file, chunk_size - 2);
3554
3555     return chunk_size;
3556   }
3557
3558   ei = &element_info[element];
3559
3560   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3561     ei->description[i] = getFile8Bit(file);
3562   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3563
3564   group = element_info[element].group;
3565
3566   group->num_elements = getFile8Bit(file);
3567
3568   ei->use_gfx_element = getFile8Bit(file);
3569   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3570
3571   group->choice_mode = getFile8Bit(file);
3572
3573   // some free bytes for future values and padding
3574   ReadUnusedBytesFromFile(file, 3);
3575
3576   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3577     group->element[i] = getMappedElement(getFile16BitBE(file));
3578
3579   // mark this group element as modified
3580   element_info[element].modified_settings = TRUE;
3581
3582   level->file_has_custom_elements = TRUE;
3583
3584   return chunk_size;
3585 }
3586
3587 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3588                                 int element, int real_element)
3589 {
3590   int micro_chunk_size = 0;
3591   int conf_type = getFile8Bit(file);
3592   int byte_mask = conf_type & CONF_MASK_BYTES;
3593   boolean element_found = FALSE;
3594   int i;
3595
3596   micro_chunk_size += 1;
3597
3598   if (byte_mask == CONF_MASK_MULTI_BYTES)
3599   {
3600     int num_bytes = getFile16BitBE(file);
3601     byte *buffer = checked_malloc(num_bytes);
3602
3603     ReadBytesFromFile(file, buffer, num_bytes);
3604
3605     for (i = 0; conf[i].data_type != -1; i++)
3606     {
3607       if (conf[i].element == element &&
3608           conf[i].conf_type == conf_type)
3609       {
3610         int data_type = conf[i].data_type;
3611         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3612         int max_num_entities = conf[i].max_num_entities;
3613
3614         if (num_entities > max_num_entities)
3615         {
3616           Warn("truncating number of entities for element %d from %d to %d",
3617                element, num_entities, max_num_entities);
3618
3619           num_entities = max_num_entities;
3620         }
3621
3622         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3623                                   data_type == TYPE_CONTENT_LIST))
3624         {
3625           // for element and content lists, zero entities are not allowed
3626           Warn("found empty list of entities for element %d", element);
3627
3628           // do not set "num_entities" here to prevent reading behind buffer
3629
3630           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3631         }
3632         else
3633         {
3634           *(int *)(conf[i].num_entities) = num_entities;
3635         }
3636
3637         element_found = TRUE;
3638
3639         if (data_type == TYPE_STRING)
3640         {
3641           char *string = (char *)(conf[i].value);
3642           int j;
3643
3644           for (j = 0; j < max_num_entities; j++)
3645             string[j] = (j < num_entities ? buffer[j] : '\0');
3646         }
3647         else if (data_type == TYPE_ELEMENT_LIST)
3648         {
3649           int *element_array = (int *)(conf[i].value);
3650           int j;
3651
3652           for (j = 0; j < num_entities; j++)
3653             element_array[j] =
3654               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3655         }
3656         else if (data_type == TYPE_CONTENT_LIST)
3657         {
3658           struct Content *content= (struct Content *)(conf[i].value);
3659           int c, x, y;
3660
3661           for (c = 0; c < num_entities; c++)
3662             for (y = 0; y < 3; y++)
3663               for (x = 0; x < 3; x++)
3664                 content[c].e[x][y] =
3665                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3666         }
3667         else
3668           element_found = FALSE;
3669
3670         break;
3671       }
3672     }
3673
3674     checked_free(buffer);
3675
3676     micro_chunk_size += 2 + num_bytes;
3677   }
3678   else          // constant size configuration data (1, 2 or 4 bytes)
3679   {
3680     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3681                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3682                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3683
3684     for (i = 0; conf[i].data_type != -1; i++)
3685     {
3686       if (conf[i].element == element &&
3687           conf[i].conf_type == conf_type)
3688       {
3689         int data_type = conf[i].data_type;
3690
3691         if (data_type == TYPE_ELEMENT)
3692           value = getMappedElement(value);
3693
3694         if (data_type == TYPE_BOOLEAN)
3695           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3696         else
3697           *(int *)    (conf[i].value) = value;
3698
3699         element_found = TRUE;
3700
3701         break;
3702       }
3703     }
3704
3705     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3706   }
3707
3708   if (!element_found)
3709   {
3710     char *error_conf_chunk_bytes =
3711       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3712        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3713        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3714     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3715     int error_element = real_element;
3716
3717     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3718          error_conf_chunk_bytes, error_conf_chunk_token,
3719          error_element, EL_NAME(error_element));
3720   }
3721
3722   return micro_chunk_size;
3723 }
3724
3725 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3726 {
3727   int real_chunk_size = 0;
3728
3729   li = *level;          // copy level data into temporary buffer
3730
3731   while (!checkEndOfFile(file))
3732   {
3733     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3734
3735     if (real_chunk_size >= chunk_size)
3736       break;
3737   }
3738
3739   *level = li;          // copy temporary buffer back to level data
3740
3741   return real_chunk_size;
3742 }
3743
3744 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3745 {
3746   int real_chunk_size = 0;
3747
3748   li = *level;          // copy level data into temporary buffer
3749
3750   while (!checkEndOfFile(file))
3751   {
3752     int element = getMappedElement(getFile16BitBE(file));
3753
3754     real_chunk_size += 2;
3755     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3756                                             element, element);
3757     if (real_chunk_size >= chunk_size)
3758       break;
3759   }
3760
3761   *level = li;          // copy temporary buffer back to level data
3762
3763   return real_chunk_size;
3764 }
3765
3766 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3767 {
3768   int real_chunk_size = 0;
3769
3770   li = *level;          // copy level data into temporary buffer
3771
3772   while (!checkEndOfFile(file))
3773   {
3774     int element = getMappedElement(getFile16BitBE(file));
3775
3776     real_chunk_size += 2;
3777     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3778                                             element, element);
3779     if (real_chunk_size >= chunk_size)
3780       break;
3781   }
3782
3783   *level = li;          // copy temporary buffer back to level data
3784
3785   return real_chunk_size;
3786 }
3787
3788 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3789 {
3790   int element = getMappedElement(getFile16BitBE(file));
3791   int envelope_nr = element - EL_ENVELOPE_1;
3792   int real_chunk_size = 2;
3793
3794   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3795
3796   while (!checkEndOfFile(file))
3797   {
3798     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3799                                             -1, element);
3800
3801     if (real_chunk_size >= chunk_size)
3802       break;
3803   }
3804
3805   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3806
3807   return real_chunk_size;
3808 }
3809
3810 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3811 {
3812   int element = getMappedElement(getFile16BitBE(file));
3813   int real_chunk_size = 2;
3814   struct ElementInfo *ei = &element_info[element];
3815   int i;
3816
3817   xx_ei = *ei;          // copy element data into temporary buffer
3818
3819   xx_ei.num_change_pages = -1;
3820
3821   while (!checkEndOfFile(file))
3822   {
3823     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3824                                             -1, element);
3825     if (xx_ei.num_change_pages != -1)
3826       break;
3827
3828     if (real_chunk_size >= chunk_size)
3829       break;
3830   }
3831
3832   *ei = xx_ei;
3833
3834   if (ei->num_change_pages == -1)
3835   {
3836     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3837          EL_NAME(element));
3838
3839     ei->num_change_pages = 1;
3840
3841     setElementChangePages(ei, 1);
3842     setElementChangeInfoToDefaults(ei->change);
3843
3844     return real_chunk_size;
3845   }
3846
3847   // initialize number of change pages stored for this custom element
3848   setElementChangePages(ei, ei->num_change_pages);
3849   for (i = 0; i < ei->num_change_pages; i++)
3850     setElementChangeInfoToDefaults(&ei->change_page[i]);
3851
3852   // start with reading properties for the first change page
3853   xx_current_change_page = 0;
3854
3855   while (!checkEndOfFile(file))
3856   {
3857     // level file might contain invalid change page number
3858     if (xx_current_change_page >= ei->num_change_pages)
3859       break;
3860
3861     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3862
3863     xx_change = *change;        // copy change data into temporary buffer
3864
3865     resetEventBits();           // reset bits; change page might have changed
3866
3867     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3868                                             -1, element);
3869
3870     *change = xx_change;
3871
3872     setEventFlagsFromEventBits(change);
3873
3874     if (real_chunk_size >= chunk_size)
3875       break;
3876   }
3877
3878   level->file_has_custom_elements = TRUE;
3879
3880   return real_chunk_size;
3881 }
3882
3883 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3884 {
3885   int element = getMappedElement(getFile16BitBE(file));
3886   int real_chunk_size = 2;
3887   struct ElementInfo *ei = &element_info[element];
3888   struct ElementGroupInfo *group = ei->group;
3889
3890   if (group == NULL)
3891     return -1;
3892
3893   xx_ei = *ei;          // copy element data into temporary buffer
3894   xx_group = *group;    // copy group data into temporary buffer
3895
3896   while (!checkEndOfFile(file))
3897   {
3898     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3899                                             -1, element);
3900
3901     if (real_chunk_size >= chunk_size)
3902       break;
3903   }
3904
3905   *ei = xx_ei;
3906   *group = xx_group;
3907
3908   level->file_has_custom_elements = TRUE;
3909
3910   return real_chunk_size;
3911 }
3912
3913 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3914 {
3915   int element = getMappedElement(getFile16BitBE(file));
3916   int real_chunk_size = 2;
3917   struct ElementInfo *ei = &element_info[element];
3918
3919   xx_ei = *ei;          // copy element data into temporary buffer
3920
3921   while (!checkEndOfFile(file))
3922   {
3923     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3924                                             -1, element);
3925
3926     if (real_chunk_size >= chunk_size)
3927       break;
3928   }
3929
3930   *ei = xx_ei;
3931
3932   level->file_has_custom_elements = TRUE;
3933
3934   return real_chunk_size;
3935 }
3936
3937 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3938                                       struct LevelFileInfo *level_file_info,
3939                                       boolean level_info_only)
3940 {
3941   char *filename = level_file_info->filename;
3942   char cookie[MAX_LINE_LEN];
3943   char chunk_name[CHUNK_ID_LEN + 1];
3944   int chunk_size;
3945   File *file;
3946
3947   if (!(file = openFile(filename, MODE_READ)))
3948   {
3949     level->no_valid_file = TRUE;
3950     level->no_level_file = TRUE;
3951
3952     if (level_info_only)
3953       return;
3954
3955     Warn("cannot read level '%s' -- using empty level", filename);
3956
3957     if (!setup.editor.use_template_for_new_levels)
3958       return;
3959
3960     // if level file not found, try to initialize level data from template
3961     filename = getGlobalLevelTemplateFilename();
3962
3963     if (!(file = openFile(filename, MODE_READ)))
3964       return;
3965
3966     // default: for empty levels, use level template for custom elements
3967     level->use_custom_template = TRUE;
3968
3969     level->no_valid_file = FALSE;
3970   }
3971
3972   getFileChunkBE(file, chunk_name, NULL);
3973   if (strEqual(chunk_name, "RND1"))
3974   {
3975     getFile32BitBE(file);               // not used
3976
3977     getFileChunkBE(file, chunk_name, NULL);
3978     if (!strEqual(chunk_name, "CAVE"))
3979     {
3980       level->no_valid_file = TRUE;
3981
3982       Warn("unknown format of level file '%s'", filename);
3983
3984       closeFile(file);
3985
3986       return;
3987     }
3988   }
3989   else  // check for pre-2.0 file format with cookie string
3990   {
3991     strcpy(cookie, chunk_name);
3992     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3993       cookie[4] = '\0';
3994     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3995       cookie[strlen(cookie) - 1] = '\0';
3996
3997     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3998     {
3999       level->no_valid_file = TRUE;
4000
4001       Warn("unknown format of level file '%s'", filename);
4002
4003       closeFile(file);
4004
4005       return;
4006     }
4007
4008     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4009     {
4010       level->no_valid_file = TRUE;
4011
4012       Warn("unsupported version of level file '%s'", filename);
4013
4014       closeFile(file);
4015
4016       return;
4017     }
4018
4019     // pre-2.0 level files have no game version, so use file version here
4020     level->game_version = level->file_version;
4021   }
4022
4023   if (level->file_version < FILE_VERSION_1_2)
4024   {
4025     // level files from versions before 1.2.0 without chunk structure
4026     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4027     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4028   }
4029   else
4030   {
4031     static struct
4032     {
4033       char *name;
4034       int size;
4035       int (*loader)(File *, int, struct LevelInfo *);
4036     }
4037     chunk_info[] =
4038     {
4039       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4040       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4041       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4042       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4043       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4044       { "INFO", -1,                     LoadLevel_INFO },
4045       { "BODY", -1,                     LoadLevel_BODY },
4046       { "CONT", -1,                     LoadLevel_CONT },
4047       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4048       { "CNT3", -1,                     LoadLevel_CNT3 },
4049       { "CUS1", -1,                     LoadLevel_CUS1 },
4050       { "CUS2", -1,                     LoadLevel_CUS2 },
4051       { "CUS3", -1,                     LoadLevel_CUS3 },
4052       { "CUS4", -1,                     LoadLevel_CUS4 },
4053       { "GRP1", -1,                     LoadLevel_GRP1 },
4054       { "CONF", -1,                     LoadLevel_CONF },
4055       { "ELEM", -1,                     LoadLevel_ELEM },
4056       { "NOTE", -1,                     LoadLevel_NOTE },
4057       { "CUSX", -1,                     LoadLevel_CUSX },
4058       { "GRPX", -1,                     LoadLevel_GRPX },
4059       { "EMPX", -1,                     LoadLevel_EMPX },
4060
4061       {  NULL,  0,                      NULL }
4062     };
4063
4064     while (getFileChunkBE(file, chunk_name, &chunk_size))
4065     {
4066       int i = 0;
4067
4068       while (chunk_info[i].name != NULL &&
4069              !strEqual(chunk_name, chunk_info[i].name))
4070         i++;
4071
4072       if (chunk_info[i].name == NULL)
4073       {
4074         Warn("unknown chunk '%s' in level file '%s'",
4075              chunk_name, filename);
4076
4077         ReadUnusedBytesFromFile(file, chunk_size);
4078       }
4079       else if (chunk_info[i].size != -1 &&
4080                chunk_info[i].size != chunk_size)
4081       {
4082         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4083              chunk_size, chunk_name, filename);
4084
4085         ReadUnusedBytesFromFile(file, chunk_size);
4086       }
4087       else
4088       {
4089         // call function to load this level chunk
4090         int chunk_size_expected =
4091           (chunk_info[i].loader)(file, chunk_size, level);
4092
4093         if (chunk_size_expected < 0)
4094         {
4095           Warn("error reading chunk '%s' in level file '%s'",
4096                chunk_name, filename);
4097
4098           break;
4099         }
4100
4101         // the size of some chunks cannot be checked before reading other
4102         // chunks first (like "HEAD" and "BODY") that contain some header
4103         // information, so check them here
4104         if (chunk_size_expected != chunk_size)
4105         {
4106           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4107                chunk_size, chunk_name, filename);
4108
4109           break;
4110         }
4111       }
4112     }
4113   }
4114
4115   closeFile(file);
4116 }
4117
4118
4119 // ----------------------------------------------------------------------------
4120 // functions for loading BD level
4121 // ----------------------------------------------------------------------------
4122
4123 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4124 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4125
4126 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4127 {
4128   struct LevelInfo_BD *level_bd = level->native_bd_level;
4129   GdCave *cave = NULL;  // will be changed below
4130   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4131   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4132   int x, y;
4133
4134   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4135
4136   // cave and map newly allocated when set to defaults above
4137   cave = level_bd->cave;
4138
4139   // level type
4140   cave->intermission                    = level->bd_intermission;
4141
4142   // level settings
4143   cave->level_time[0]                   = level->time;
4144   cave->level_diamonds[0]               = level->gems_needed;
4145
4146   // game timing
4147   cave->scheduling                      = level->bd_scheduling_type;
4148   cave->pal_timing                      = level->bd_pal_timing;
4149   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4150   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4151   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4152   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4153
4154   // scores
4155   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4156   cave->diamond_value                   = level->score[SC_EMERALD];
4157   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4158
4159   // compatibility settings
4160   cave->lineshift                       = level->bd_line_shifting_borders;
4161   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4162   cave->short_explosions                = level->bd_short_explosions;
4163   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4164
4165   // player properties
4166   cave->diagonal_movements              = level->bd_diagonal_movements;
4167   cave->active_is_first_found           = level->bd_topmost_player_active;
4168   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4169   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4170   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4171   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4172
4173   // element properties
4174   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4175   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4176   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4177   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4178   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4179   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4180   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4181   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4182   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4183
4184   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4185   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4186   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4187   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4188   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4189   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4190   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4191
4192   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4193   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4194   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4195   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4196   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4197   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4198   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4199   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4200   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4201   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4202   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4203
4204   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4205   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4206   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4207   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4208   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4209   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4210
4211   cave->slime_predictable               = level->bd_slime_is_predictable;
4212   cave->slime_correct_random            = level->bd_slime_correct_random;
4213   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4214   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4215   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4216   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4217   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4218   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4219   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4220   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4221   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4222   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4223
4224   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4225   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4226   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4227
4228   cave->biter_delay_frame               = level->bd_biter_move_delay;
4229   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4230
4231   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4232
4233   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4234
4235   cave->replicators_active              = level->bd_replicators_active;
4236   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4237
4238   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4239   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4240
4241   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4242
4243   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4244
4245   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4246   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4247   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4248
4249   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4250   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4251
4252   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4253   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4254
4255   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4256   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4257   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4258
4259   cave->gravity                         = level->bd_gravity_direction;
4260   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4261   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4262
4263   // level name
4264   strncpy(cave->name, level->name, sizeof(GdString));
4265   cave->name[sizeof(GdString) - 1] = '\0';
4266
4267   // playfield elements
4268   for (x = 0; x < cave->w; x++)
4269     for (y = 0; y < cave->h; y++)
4270       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4271 }
4272
4273 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4274 {
4275   struct LevelInfo_BD *level_bd = level->native_bd_level;
4276   GdCave *cave = level_bd->cave;
4277   int bd_level_nr = level_bd->level_nr;
4278   int x, y;
4279
4280   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4281   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4282
4283   // level type
4284   level->bd_intermission                = cave->intermission;
4285
4286   // level settings
4287   level->time                           = cave->level_time[bd_level_nr];
4288   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4289
4290   // game timing
4291   level->bd_scheduling_type             = cave->scheduling;
4292   level->bd_pal_timing                  = cave->pal_timing;
4293   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4294   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4295   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4296   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4297
4298   // scores
4299   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4300   level->score[SC_EMERALD]              = cave->diamond_value;
4301   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4302
4303   // compatibility settings
4304   level->bd_line_shifting_borders       = cave->lineshift;
4305   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4306   level->bd_short_explosions            = cave->short_explosions;
4307   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4308
4309   // player properties
4310   level->bd_diagonal_movements          = cave->diagonal_movements;
4311   level->bd_topmost_player_active       = cave->active_is_first_found;
4312   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4313   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4314   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4315   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4316
4317   // element properties
4318   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4319   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4320   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4321   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4322   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4323   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4324   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4325   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4326   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4327
4328   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4329   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4330   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4331   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4332   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4333   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4334   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4335
4336   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4337   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4338   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4339   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4340   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4341   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4342   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4343   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4344   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4345   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4346   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4347
4348   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4349   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4350   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4351   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4352   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4353   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4354
4355   level->bd_slime_is_predictable        = cave->slime_predictable;
4356   level->bd_slime_correct_random        = cave->slime_correct_random;
4357   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4358   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4359   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4360   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4361   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4362   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4363   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4364   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4365   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4366   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4367
4368   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4369   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4370   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4371
4372   level->bd_biter_move_delay            = cave->biter_delay_frame;
4373   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4374
4375   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4376
4377   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4378
4379   level->bd_replicators_active          = cave->replicators_active;
4380   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4381
4382   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4383   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4384
4385   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4386
4387   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4388
4389   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4390   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4391   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4392
4393   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4394   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4395
4396   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4397   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4398
4399   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4400   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4401   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4402
4403   level->bd_gravity_direction           = cave->gravity;
4404   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4405   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4406
4407   // level name
4408   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4409
4410   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4411   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4412
4413   // playfield elements
4414   for (x = 0; x < level->fieldx; x++)
4415     for (y = 0; y < level->fieldy; y++)
4416       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4417
4418   checked_free(cave_name);
4419 }
4420
4421 static void setTapeInfoToDefaults(void);
4422
4423 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4424 {
4425   struct LevelInfo_BD *level_bd = level->native_bd_level;
4426   GdCave *cave = level_bd->cave;
4427   GdReplay *replay = level_bd->replay;
4428   int i;
4429
4430   if (replay == NULL)
4431     return;
4432
4433   // always start with reliable default values
4434   setTapeInfoToDefaults();
4435
4436   tape.level_nr = level_nr;             // (currently not used)
4437   tape.random_seed = replay->seed;
4438
4439   TapeSetDateFromIsoDateString(replay->date);
4440
4441   tape.counter = 0;
4442   tape.pos[tape.counter].delay = 0;
4443
4444   tape.bd_replay = TRUE;
4445
4446   // all time calculations only used to display approximate tape time
4447   int cave_speed = cave->speed;
4448   int milliseconds_game = 0;
4449   int milliseconds_elapsed = 20;
4450
4451   for (i = 0; i < replay->movements->len; i++)
4452   {
4453     int replay_action = replay->movements->data[i];
4454     int tape_action = map_action_BD_to_RND(replay_action);
4455     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4456     boolean success = 0;
4457
4458     while (1)
4459     {
4460       success = TapeAddAction(action);
4461
4462       milliseconds_game += milliseconds_elapsed;
4463
4464       if (milliseconds_game >= cave_speed)
4465       {
4466         milliseconds_game -= cave_speed;
4467
4468         break;
4469       }
4470     }
4471
4472     tape.counter++;
4473     tape.pos[tape.counter].delay = 0;
4474     tape.pos[tape.counter].action[0] = 0;
4475
4476     if (!success)
4477     {
4478       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4479
4480       break;
4481     }
4482   }
4483
4484   TapeHaltRecording();
4485 }
4486
4487
4488 // ----------------------------------------------------------------------------
4489 // functions for loading EM level
4490 // ----------------------------------------------------------------------------
4491
4492 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4493 {
4494   static int ball_xy[8][2] =
4495   {
4496     { 0, 0 },
4497     { 1, 0 },
4498     { 2, 0 },
4499     { 0, 1 },
4500     { 2, 1 },
4501     { 0, 2 },
4502     { 1, 2 },
4503     { 2, 2 },
4504   };
4505   struct LevelInfo_EM *level_em = level->native_em_level;
4506   struct CAVE *cav = level_em->cav;
4507   int i, j, x, y;
4508
4509   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4510   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4511
4512   cav->time_seconds     = level->time;
4513   cav->gems_needed      = level->gems_needed;
4514
4515   cav->emerald_score    = level->score[SC_EMERALD];
4516   cav->diamond_score    = level->score[SC_DIAMOND];
4517   cav->alien_score      = level->score[SC_ROBOT];
4518   cav->tank_score       = level->score[SC_SPACESHIP];
4519   cav->bug_score        = level->score[SC_BUG];
4520   cav->eater_score      = level->score[SC_YAMYAM];
4521   cav->nut_score        = level->score[SC_NUT];
4522   cav->dynamite_score   = level->score[SC_DYNAMITE];
4523   cav->key_score        = level->score[SC_KEY];
4524   cav->exit_score       = level->score[SC_TIME_BONUS];
4525
4526   cav->num_eater_arrays = level->num_yamyam_contents;
4527
4528   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4529     for (y = 0; y < 3; y++)
4530       for (x = 0; x < 3; x++)
4531         cav->eater_array[i][y * 3 + x] =
4532           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4533
4534   cav->amoeba_time              = level->amoeba_speed;
4535   cav->wonderwall_time          = level->time_magic_wall;
4536   cav->wheel_time               = level->time_wheel;
4537
4538   cav->android_move_time        = level->android_move_time;
4539   cav->android_clone_time       = level->android_clone_time;
4540   cav->ball_random              = level->ball_random;
4541   cav->ball_active              = level->ball_active_initial;
4542   cav->ball_time                = level->ball_time;
4543   cav->num_ball_arrays          = level->num_ball_contents;
4544
4545   cav->lenses_score             = level->lenses_score;
4546   cav->magnify_score            = level->magnify_score;
4547   cav->slurp_score              = level->slurp_score;
4548
4549   cav->lenses_time              = level->lenses_time;
4550   cav->magnify_time             = level->magnify_time;
4551
4552   cav->wind_time = 9999;
4553   cav->wind_direction =
4554     map_direction_RND_to_EM(level->wind_direction_initial);
4555
4556   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4557     for (j = 0; j < 8; j++)
4558       cav->ball_array[i][j] =
4559         map_element_RND_to_EM_cave(level->ball_content[i].
4560                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4561
4562   map_android_clone_elements_RND_to_EM(level);
4563
4564   // first fill the complete playfield with the empty space element
4565   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4566     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4567       cav->cave[x][y] = Cblank;
4568
4569   // then copy the real level contents from level file into the playfield
4570   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4571   {
4572     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4573
4574     if (level->field[x][y] == EL_AMOEBA_DEAD)
4575       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4576
4577     cav->cave[x][y] = new_element;
4578   }
4579
4580   for (i = 0; i < MAX_PLAYERS; i++)
4581   {
4582     cav->player_x[i] = -1;
4583     cav->player_y[i] = -1;
4584   }
4585
4586   // initialize player positions and delete players from the playfield
4587   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4588   {
4589     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4590     {
4591       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4592
4593       cav->player_x[player_nr] = x;
4594       cav->player_y[player_nr] = y;
4595
4596       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4597     }
4598   }
4599 }
4600
4601 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4602 {
4603   static int ball_xy[8][2] =
4604   {
4605     { 0, 0 },
4606     { 1, 0 },
4607     { 2, 0 },
4608     { 0, 1 },
4609     { 2, 1 },
4610     { 0, 2 },
4611     { 1, 2 },
4612     { 2, 2 },
4613   };
4614   struct LevelInfo_EM *level_em = level->native_em_level;
4615   struct CAVE *cav = level_em->cav;
4616   int i, j, x, y;
4617
4618   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4619   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4620
4621   level->time        = cav->time_seconds;
4622   level->gems_needed = cav->gems_needed;
4623
4624   sprintf(level->name, "Level %d", level->file_info.nr);
4625
4626   level->score[SC_EMERALD]      = cav->emerald_score;
4627   level->score[SC_DIAMOND]      = cav->diamond_score;
4628   level->score[SC_ROBOT]        = cav->alien_score;
4629   level->score[SC_SPACESHIP]    = cav->tank_score;
4630   level->score[SC_BUG]          = cav->bug_score;
4631   level->score[SC_YAMYAM]       = cav->eater_score;
4632   level->score[SC_NUT]          = cav->nut_score;
4633   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4634   level->score[SC_KEY]          = cav->key_score;
4635   level->score[SC_TIME_BONUS]   = cav->exit_score;
4636
4637   level->num_yamyam_contents    = cav->num_eater_arrays;
4638
4639   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4640     for (y = 0; y < 3; y++)
4641       for (x = 0; x < 3; x++)
4642         level->yamyam_content[i].e[x][y] =
4643           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4644
4645   level->amoeba_speed           = cav->amoeba_time;
4646   level->time_magic_wall        = cav->wonderwall_time;
4647   level->time_wheel             = cav->wheel_time;
4648
4649   level->android_move_time      = cav->android_move_time;
4650   level->android_clone_time     = cav->android_clone_time;
4651   level->ball_random            = cav->ball_random;
4652   level->ball_active_initial    = cav->ball_active;
4653   level->ball_time              = cav->ball_time;
4654   level->num_ball_contents      = cav->num_ball_arrays;
4655
4656   level->lenses_score           = cav->lenses_score;
4657   level->magnify_score          = cav->magnify_score;
4658   level->slurp_score            = cav->slurp_score;
4659
4660   level->lenses_time            = cav->lenses_time;
4661   level->magnify_time           = cav->magnify_time;
4662
4663   level->wind_direction_initial =
4664     map_direction_EM_to_RND(cav->wind_direction);
4665
4666   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4667     for (j = 0; j < 8; j++)
4668       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4669         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4670
4671   map_android_clone_elements_EM_to_RND(level);
4672
4673   // convert the playfield (some elements need special treatment)
4674   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4675   {
4676     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4677
4678     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4679       new_element = EL_AMOEBA_DEAD;
4680
4681     level->field[x][y] = new_element;
4682   }
4683
4684   for (i = 0; i < MAX_PLAYERS; i++)
4685   {
4686     // in case of all players set to the same field, use the first player
4687     int nr = MAX_PLAYERS - i - 1;
4688     int jx = cav->player_x[nr];
4689     int jy = cav->player_y[nr];
4690
4691     if (jx != -1 && jy != -1)
4692       level->field[jx][jy] = EL_PLAYER_1 + nr;
4693   }
4694
4695   // time score is counted for each 10 seconds left in Emerald Mine levels
4696   level->time_score_base = 10;
4697 }
4698
4699
4700 // ----------------------------------------------------------------------------
4701 // functions for loading SP level
4702 // ----------------------------------------------------------------------------
4703
4704 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4705 {
4706   struct LevelInfo_SP *level_sp = level->native_sp_level;
4707   LevelInfoType *header = &level_sp->header;
4708   int i, x, y;
4709
4710   level_sp->width  = level->fieldx;
4711   level_sp->height = level->fieldy;
4712
4713   for (x = 0; x < level->fieldx; x++)
4714     for (y = 0; y < level->fieldy; y++)
4715       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4716
4717   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4718
4719   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4720     header->LevelTitle[i] = level->name[i];
4721   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4722
4723   header->InfotronsNeeded = level->gems_needed;
4724
4725   header->SpecialPortCount = 0;
4726
4727   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4728   {
4729     boolean gravity_port_found = FALSE;
4730     boolean gravity_port_valid = FALSE;
4731     int gravity_port_flag;
4732     int gravity_port_base_element;
4733     int element = level->field[x][y];
4734
4735     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4736         element <= EL_SP_GRAVITY_ON_PORT_UP)
4737     {
4738       gravity_port_found = TRUE;
4739       gravity_port_valid = TRUE;
4740       gravity_port_flag = 1;
4741       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4742     }
4743     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4744              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4745     {
4746       gravity_port_found = TRUE;
4747       gravity_port_valid = TRUE;
4748       gravity_port_flag = 0;
4749       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4750     }
4751     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4752              element <= EL_SP_GRAVITY_PORT_UP)
4753     {
4754       // change R'n'D style gravity inverting special port to normal port
4755       // (there are no gravity inverting ports in native Supaplex engine)
4756
4757       gravity_port_found = TRUE;
4758       gravity_port_valid = FALSE;
4759       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4760     }
4761
4762     if (gravity_port_found)
4763     {
4764       if (gravity_port_valid &&
4765           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4766       {
4767         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4768
4769         port->PortLocation = (y * level->fieldx + x) * 2;
4770         port->Gravity = gravity_port_flag;
4771
4772         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4773
4774         header->SpecialPortCount++;
4775       }
4776       else
4777       {
4778         // change special gravity port to normal port
4779
4780         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4781       }
4782
4783       level_sp->playfield[x][y] = element - EL_SP_START;
4784     }
4785   }
4786 }
4787
4788 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4789 {
4790   struct LevelInfo_SP *level_sp = level->native_sp_level;
4791   LevelInfoType *header = &level_sp->header;
4792   boolean num_invalid_elements = 0;
4793   int i, j, x, y;
4794
4795   level->fieldx = level_sp->width;
4796   level->fieldy = level_sp->height;
4797
4798   for (x = 0; x < level->fieldx; x++)
4799   {
4800     for (y = 0; y < level->fieldy; y++)
4801     {
4802       int element_old = level_sp->playfield[x][y];
4803       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4804
4805       if (element_new == EL_UNKNOWN)
4806       {
4807         num_invalid_elements++;
4808
4809         Debug("level:native:SP", "invalid element %d at position %d, %d",
4810               element_old, x, y);
4811       }
4812
4813       level->field[x][y] = element_new;
4814     }
4815   }
4816
4817   if (num_invalid_elements > 0)
4818     Warn("found %d invalid elements%s", num_invalid_elements,
4819          (!options.debug ? " (use '--debug' for more details)" : ""));
4820
4821   for (i = 0; i < MAX_PLAYERS; i++)
4822     level->initial_player_gravity[i] =
4823       (header->InitialGravity == 1 ? TRUE : FALSE);
4824
4825   // skip leading spaces
4826   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4827     if (header->LevelTitle[i] != ' ')
4828       break;
4829
4830   // copy level title
4831   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4832     level->name[j] = header->LevelTitle[i];
4833   level->name[j] = '\0';
4834
4835   // cut trailing spaces
4836   for (; j > 0; j--)
4837     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4838       level->name[j - 1] = '\0';
4839
4840   level->gems_needed = header->InfotronsNeeded;
4841
4842   for (i = 0; i < header->SpecialPortCount; i++)
4843   {
4844     SpecialPortType *port = &header->SpecialPort[i];
4845     int port_location = port->PortLocation;
4846     int gravity = port->Gravity;
4847     int port_x, port_y, port_element;
4848
4849     port_x = (port_location / 2) % level->fieldx;
4850     port_y = (port_location / 2) / level->fieldx;
4851
4852     if (port_x < 0 || port_x >= level->fieldx ||
4853         port_y < 0 || port_y >= level->fieldy)
4854     {
4855       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4856
4857       continue;
4858     }
4859
4860     port_element = level->field[port_x][port_y];
4861
4862     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4863         port_element > EL_SP_GRAVITY_PORT_UP)
4864     {
4865       Warn("no special port at position (%d, %d)", port_x, port_y);
4866
4867       continue;
4868     }
4869
4870     // change previous (wrong) gravity inverting special port to either
4871     // gravity enabling special port or gravity disabling special port
4872     level->field[port_x][port_y] +=
4873       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4874        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4875   }
4876
4877   // change special gravity ports without database entries to normal ports
4878   for (x = 0; x < level->fieldx; x++)
4879     for (y = 0; y < level->fieldy; y++)
4880       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4881           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4882         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4883
4884   level->time = 0;                      // no time limit
4885   level->amoeba_speed = 0;
4886   level->time_magic_wall = 0;
4887   level->time_wheel = 0;
4888   level->amoeba_content = EL_EMPTY;
4889
4890   // original Supaplex does not use score values -- rate by playing time
4891   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4892     level->score[i] = 0;
4893
4894   level->rate_time_over_score = TRUE;
4895
4896   // there are no yamyams in supaplex levels
4897   for (i = 0; i < level->num_yamyam_contents; i++)
4898     for (x = 0; x < 3; x++)
4899       for (y = 0; y < 3; y++)
4900         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4901 }
4902
4903 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4904 {
4905   struct LevelInfo_SP *level_sp = level->native_sp_level;
4906   struct DemoInfo_SP *demo = &level_sp->demo;
4907   int i, j;
4908
4909   // always start with reliable default values
4910   demo->is_available = FALSE;
4911   demo->length = 0;
4912
4913   if (TAPE_IS_EMPTY(tape))
4914     return;
4915
4916   demo->level_nr = tape.level_nr;       // (currently not used)
4917
4918   level_sp->header.DemoRandomSeed = tape.random_seed;
4919
4920   demo->length = 0;
4921
4922   for (i = 0; i < tape.length; i++)
4923   {
4924     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4925     int demo_repeat = tape.pos[i].delay;
4926     int demo_entries = (demo_repeat + 15) / 16;
4927
4928     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4929     {
4930       Warn("tape truncated: size exceeds maximum SP demo size %d",
4931            SP_MAX_TAPE_LEN);
4932
4933       break;
4934     }
4935
4936     for (j = 0; j < demo_repeat / 16; j++)
4937       demo->data[demo->length++] = 0xf0 | demo_action;
4938
4939     if (demo_repeat % 16)
4940       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4941   }
4942
4943   demo->is_available = TRUE;
4944 }
4945
4946 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4947 {
4948   struct LevelInfo_SP *level_sp = level->native_sp_level;
4949   struct DemoInfo_SP *demo = &level_sp->demo;
4950   char *filename = level->file_info.filename;
4951   int i;
4952
4953   // always start with reliable default values
4954   setTapeInfoToDefaults();
4955
4956   if (!demo->is_available)
4957     return;
4958
4959   tape.level_nr = demo->level_nr;       // (currently not used)
4960   tape.random_seed = level_sp->header.DemoRandomSeed;
4961
4962   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4963
4964   tape.counter = 0;
4965   tape.pos[tape.counter].delay = 0;
4966
4967   for (i = 0; i < demo->length; i++)
4968   {
4969     int demo_action = demo->data[i] & 0x0f;
4970     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4971     int tape_action = map_key_SP_to_RND(demo_action);
4972     int tape_repeat = demo_repeat + 1;
4973     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4974     boolean success = 0;
4975     int j;
4976
4977     for (j = 0; j < tape_repeat; j++)
4978       success = TapeAddAction(action);
4979
4980     if (!success)
4981     {
4982       Warn("SP demo truncated: size exceeds maximum tape size %d",
4983            MAX_TAPE_LEN);
4984
4985       break;
4986     }
4987   }
4988
4989   TapeHaltRecording();
4990 }
4991
4992
4993 // ----------------------------------------------------------------------------
4994 // functions for loading MM level
4995 // ----------------------------------------------------------------------------
4996
4997 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4998 {
4999   struct LevelInfo_MM *level_mm = level->native_mm_level;
5000   int i, x, y;
5001
5002   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5003   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5004
5005   level_mm->time = level->time;
5006   level_mm->kettles_needed = level->gems_needed;
5007   level_mm->auto_count_kettles = level->auto_count_gems;
5008
5009   level_mm->mm_laser_red   = level->mm_laser_red;
5010   level_mm->mm_laser_green = level->mm_laser_green;
5011   level_mm->mm_laser_blue  = level->mm_laser_blue;
5012
5013   level_mm->df_laser_red   = level->df_laser_red;
5014   level_mm->df_laser_green = level->df_laser_green;
5015   level_mm->df_laser_blue  = level->df_laser_blue;
5016
5017   strcpy(level_mm->name, level->name);
5018   strcpy(level_mm->author, level->author);
5019
5020   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5021   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5022   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5023   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5024   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5025
5026   level_mm->amoeba_speed = level->amoeba_speed;
5027   level_mm->time_fuse    = level->mm_time_fuse;
5028   level_mm->time_bomb    = level->mm_time_bomb;
5029   level_mm->time_ball    = level->mm_time_ball;
5030   level_mm->time_block   = level->mm_time_block;
5031
5032   level_mm->num_ball_contents = level->num_mm_ball_contents;
5033   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5034   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5035   level_mm->explode_ball = level->explode_mm_ball;
5036
5037   for (i = 0; i < level->num_mm_ball_contents; i++)
5038     level_mm->ball_content[i] =
5039       map_element_RND_to_MM(level->mm_ball_content[i]);
5040
5041   for (x = 0; x < level->fieldx; x++)
5042     for (y = 0; y < level->fieldy; y++)
5043       Ur[x][y] =
5044         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5045 }
5046
5047 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5048 {
5049   struct LevelInfo_MM *level_mm = level->native_mm_level;
5050   int i, x, y;
5051
5052   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5053   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5054
5055   level->time = level_mm->time;
5056   level->gems_needed = level_mm->kettles_needed;
5057   level->auto_count_gems = level_mm->auto_count_kettles;
5058
5059   level->mm_laser_red   = level_mm->mm_laser_red;
5060   level->mm_laser_green = level_mm->mm_laser_green;
5061   level->mm_laser_blue  = level_mm->mm_laser_blue;
5062
5063   level->df_laser_red   = level_mm->df_laser_red;
5064   level->df_laser_green = level_mm->df_laser_green;
5065   level->df_laser_blue  = level_mm->df_laser_blue;
5066
5067   strcpy(level->name, level_mm->name);
5068
5069   // only overwrite author from 'levelinfo.conf' if author defined in level
5070   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5071     strcpy(level->author, level_mm->author);
5072
5073   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5074   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5075   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5076   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5077   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5078
5079   level->amoeba_speed  = level_mm->amoeba_speed;
5080   level->mm_time_fuse  = level_mm->time_fuse;
5081   level->mm_time_bomb  = level_mm->time_bomb;
5082   level->mm_time_ball  = level_mm->time_ball;
5083   level->mm_time_block = level_mm->time_block;
5084
5085   level->num_mm_ball_contents = level_mm->num_ball_contents;
5086   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5087   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5088   level->explode_mm_ball = level_mm->explode_ball;
5089
5090   for (i = 0; i < level->num_mm_ball_contents; i++)
5091     level->mm_ball_content[i] =
5092       map_element_MM_to_RND(level_mm->ball_content[i]);
5093
5094   for (x = 0; x < level->fieldx; x++)
5095     for (y = 0; y < level->fieldy; y++)
5096       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5097 }
5098
5099
5100 // ----------------------------------------------------------------------------
5101 // functions for loading DC level
5102 // ----------------------------------------------------------------------------
5103
5104 #define DC_LEVEL_HEADER_SIZE            344
5105
5106 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5107                                         boolean init)
5108 {
5109   static int last_data_encoded;
5110   static int offset1;
5111   static int offset2;
5112   int diff;
5113   int diff_hi, diff_lo;
5114   int data_hi, data_lo;
5115   unsigned short data_decoded;
5116
5117   if (init)
5118   {
5119     last_data_encoded = 0;
5120     offset1 = -1;
5121     offset2 = 0;
5122
5123     return 0;
5124   }
5125
5126   diff = data_encoded - last_data_encoded;
5127   diff_hi = diff & ~0xff;
5128   diff_lo = diff &  0xff;
5129
5130   offset2 += diff_lo;
5131
5132   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5133   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5134   data_hi = data_hi & 0xff00;
5135
5136   data_decoded = data_hi | data_lo;
5137
5138   last_data_encoded = data_encoded;
5139
5140   offset1 = (offset1 + 1) % 31;
5141   offset2 = offset2 & 0xff;
5142
5143   return data_decoded;
5144 }
5145
5146 static int getMappedElement_DC(int element)
5147 {
5148   switch (element)
5149   {
5150     case 0x0000:
5151       element = EL_ROCK;
5152       break;
5153
5154       // 0x0117 - 0x036e: (?)
5155       // EL_DIAMOND
5156
5157       // 0x042d - 0x0684: (?)
5158       // EL_EMERALD
5159
5160     case 0x06f1:
5161       element = EL_NUT;
5162       break;
5163
5164     case 0x074c:
5165       element = EL_BOMB;
5166       break;
5167
5168     case 0x07a4:
5169       element = EL_PEARL;
5170       break;
5171
5172     case 0x0823:
5173       element = EL_CRYSTAL;
5174       break;
5175
5176     case 0x0e77:        // quicksand (boulder)
5177       element = EL_QUICKSAND_FAST_FULL;
5178       break;
5179
5180     case 0x0e99:        // slow quicksand (boulder)
5181       element = EL_QUICKSAND_FULL;
5182       break;
5183
5184     case 0x0ed2:
5185       element = EL_EM_EXIT_OPEN;
5186       break;
5187
5188     case 0x0ee3:
5189       element = EL_EM_EXIT_CLOSED;
5190       break;
5191
5192     case 0x0eeb:
5193       element = EL_EM_STEEL_EXIT_OPEN;
5194       break;
5195
5196     case 0x0efc:
5197       element = EL_EM_STEEL_EXIT_CLOSED;
5198       break;
5199
5200     case 0x0f4f:        // dynamite (lit 1)
5201       element = EL_EM_DYNAMITE_ACTIVE;
5202       break;
5203
5204     case 0x0f57:        // dynamite (lit 2)
5205       element = EL_EM_DYNAMITE_ACTIVE;
5206       break;
5207
5208     case 0x0f5f:        // dynamite (lit 3)
5209       element = EL_EM_DYNAMITE_ACTIVE;
5210       break;
5211
5212     case 0x0f67:        // dynamite (lit 4)
5213       element = EL_EM_DYNAMITE_ACTIVE;
5214       break;
5215
5216     case 0x0f81:
5217     case 0x0f82:
5218     case 0x0f83:
5219     case 0x0f84:
5220       element = EL_AMOEBA_WET;
5221       break;
5222
5223     case 0x0f85:
5224       element = EL_AMOEBA_DROP;
5225       break;
5226
5227     case 0x0fb9:
5228       element = EL_DC_MAGIC_WALL;
5229       break;
5230
5231     case 0x0fd0:
5232       element = EL_SPACESHIP_UP;
5233       break;
5234
5235     case 0x0fd9:
5236       element = EL_SPACESHIP_DOWN;
5237       break;
5238
5239     case 0x0ff1:
5240       element = EL_SPACESHIP_LEFT;
5241       break;
5242
5243     case 0x0ff9:
5244       element = EL_SPACESHIP_RIGHT;
5245       break;
5246
5247     case 0x1057:
5248       element = EL_BUG_UP;
5249       break;
5250
5251     case 0x1060:
5252       element = EL_BUG_DOWN;
5253       break;
5254
5255     case 0x1078:
5256       element = EL_BUG_LEFT;
5257       break;
5258
5259     case 0x1080:
5260       element = EL_BUG_RIGHT;
5261       break;
5262
5263     case 0x10de:
5264       element = EL_MOLE_UP;
5265       break;
5266
5267     case 0x10e7:
5268       element = EL_MOLE_DOWN;
5269       break;
5270
5271     case 0x10ff:
5272       element = EL_MOLE_LEFT;
5273       break;
5274
5275     case 0x1107:
5276       element = EL_MOLE_RIGHT;
5277       break;
5278
5279     case 0x11c0:
5280       element = EL_ROBOT;
5281       break;
5282
5283     case 0x13f5:
5284       element = EL_YAMYAM_UP;
5285       break;
5286
5287     case 0x1425:
5288       element = EL_SWITCHGATE_OPEN;
5289       break;
5290
5291     case 0x1426:
5292       element = EL_SWITCHGATE_CLOSED;
5293       break;
5294
5295     case 0x1437:
5296       element = EL_DC_SWITCHGATE_SWITCH_UP;
5297       break;
5298
5299     case 0x143a:
5300       element = EL_TIMEGATE_CLOSED;
5301       break;
5302
5303     case 0x144c:        // conveyor belt switch (green)
5304       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5305       break;
5306
5307     case 0x144f:        // conveyor belt switch (red)
5308       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5309       break;
5310
5311     case 0x1452:        // conveyor belt switch (blue)
5312       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5313       break;
5314
5315     case 0x145b:
5316       element = EL_CONVEYOR_BELT_3_MIDDLE;
5317       break;
5318
5319     case 0x1463:
5320       element = EL_CONVEYOR_BELT_3_LEFT;
5321       break;
5322
5323     case 0x146b:
5324       element = EL_CONVEYOR_BELT_3_RIGHT;
5325       break;
5326
5327     case 0x1473:
5328       element = EL_CONVEYOR_BELT_1_MIDDLE;
5329       break;
5330
5331     case 0x147b:
5332       element = EL_CONVEYOR_BELT_1_LEFT;
5333       break;
5334
5335     case 0x1483:
5336       element = EL_CONVEYOR_BELT_1_RIGHT;
5337       break;
5338
5339     case 0x148b:
5340       element = EL_CONVEYOR_BELT_4_MIDDLE;
5341       break;
5342
5343     case 0x1493:
5344       element = EL_CONVEYOR_BELT_4_LEFT;
5345       break;
5346
5347     case 0x149b:
5348       element = EL_CONVEYOR_BELT_4_RIGHT;
5349       break;
5350
5351     case 0x14ac:
5352       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5353       break;
5354
5355     case 0x14bd:
5356       element = EL_EXPANDABLE_WALL_VERTICAL;
5357       break;
5358
5359     case 0x14c6:
5360       element = EL_EXPANDABLE_WALL_ANY;
5361       break;
5362
5363     case 0x14ce:        // growing steel wall (left/right)
5364       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5365       break;
5366
5367     case 0x14df:        // growing steel wall (up/down)
5368       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5369       break;
5370
5371     case 0x14e8:        // growing steel wall (up/down/left/right)
5372       element = EL_EXPANDABLE_STEELWALL_ANY;
5373       break;
5374
5375     case 0x14e9:
5376       element = EL_SHIELD_DEADLY;
5377       break;
5378
5379     case 0x1501:
5380       element = EL_EXTRA_TIME;
5381       break;
5382
5383     case 0x154f:
5384       element = EL_ACID;
5385       break;
5386
5387     case 0x1577:
5388       element = EL_EMPTY_SPACE;
5389       break;
5390
5391     case 0x1578:        // quicksand (empty)
5392       element = EL_QUICKSAND_FAST_EMPTY;
5393       break;
5394
5395     case 0x1579:        // slow quicksand (empty)
5396       element = EL_QUICKSAND_EMPTY;
5397       break;
5398
5399       // 0x157c - 0x158b:
5400       // EL_SAND
5401
5402       // 0x1590 - 0x159f:
5403       // EL_DC_LANDMINE
5404
5405     case 0x15a0:
5406       element = EL_EM_DYNAMITE;
5407       break;
5408
5409     case 0x15a1:        // key (red)
5410       element = EL_EM_KEY_1;
5411       break;
5412
5413     case 0x15a2:        // key (yellow)
5414       element = EL_EM_KEY_2;
5415       break;
5416
5417     case 0x15a3:        // key (blue)
5418       element = EL_EM_KEY_4;
5419       break;
5420
5421     case 0x15a4:        // key (green)
5422       element = EL_EM_KEY_3;
5423       break;
5424
5425     case 0x15a5:        // key (white)
5426       element = EL_DC_KEY_WHITE;
5427       break;
5428
5429     case 0x15a6:
5430       element = EL_WALL_SLIPPERY;
5431       break;
5432
5433     case 0x15a7:
5434       element = EL_WALL;
5435       break;
5436
5437     case 0x15a8:        // wall (not round)
5438       element = EL_WALL;
5439       break;
5440
5441     case 0x15a9:        // (blue)
5442       element = EL_CHAR_A;
5443       break;
5444
5445     case 0x15aa:        // (blue)
5446       element = EL_CHAR_B;
5447       break;
5448
5449     case 0x15ab:        // (blue)
5450       element = EL_CHAR_C;
5451       break;
5452
5453     case 0x15ac:        // (blue)
5454       element = EL_CHAR_D;
5455       break;
5456
5457     case 0x15ad:        // (blue)
5458       element = EL_CHAR_E;
5459       break;
5460
5461     case 0x15ae:        // (blue)
5462       element = EL_CHAR_F;
5463       break;
5464
5465     case 0x15af:        // (blue)
5466       element = EL_CHAR_G;
5467       break;
5468
5469     case 0x15b0:        // (blue)
5470       element = EL_CHAR_H;
5471       break;
5472
5473     case 0x15b1:        // (blue)
5474       element = EL_CHAR_I;
5475       break;
5476
5477     case 0x15b2:        // (blue)
5478       element = EL_CHAR_J;
5479       break;
5480
5481     case 0x15b3:        // (blue)
5482       element = EL_CHAR_K;
5483       break;
5484
5485     case 0x15b4:        // (blue)
5486       element = EL_CHAR_L;
5487       break;
5488
5489     case 0x15b5:        // (blue)
5490       element = EL_CHAR_M;
5491       break;
5492
5493     case 0x15b6:        // (blue)
5494       element = EL_CHAR_N;
5495       break;
5496
5497     case 0x15b7:        // (blue)
5498       element = EL_CHAR_O;
5499       break;
5500
5501     case 0x15b8:        // (blue)
5502       element = EL_CHAR_P;
5503       break;
5504
5505     case 0x15b9:        // (blue)
5506       element = EL_CHAR_Q;
5507       break;
5508
5509     case 0x15ba:        // (blue)
5510       element = EL_CHAR_R;
5511       break;
5512
5513     case 0x15bb:        // (blue)
5514       element = EL_CHAR_S;
5515       break;
5516
5517     case 0x15bc:        // (blue)
5518       element = EL_CHAR_T;
5519       break;
5520
5521     case 0x15bd:        // (blue)
5522       element = EL_CHAR_U;
5523       break;
5524
5525     case 0x15be:        // (blue)
5526       element = EL_CHAR_V;
5527       break;
5528
5529     case 0x15bf:        // (blue)
5530       element = EL_CHAR_W;
5531       break;
5532
5533     case 0x15c0:        // (blue)
5534       element = EL_CHAR_X;
5535       break;
5536
5537     case 0x15c1:        // (blue)
5538       element = EL_CHAR_Y;
5539       break;
5540
5541     case 0x15c2:        // (blue)
5542       element = EL_CHAR_Z;
5543       break;
5544
5545     case 0x15c3:        // (blue)
5546       element = EL_CHAR_AUMLAUT;
5547       break;
5548
5549     case 0x15c4:        // (blue)
5550       element = EL_CHAR_OUMLAUT;
5551       break;
5552
5553     case 0x15c5:        // (blue)
5554       element = EL_CHAR_UUMLAUT;
5555       break;
5556
5557     case 0x15c6:        // (blue)
5558       element = EL_CHAR_0;
5559       break;
5560
5561     case 0x15c7:        // (blue)
5562       element = EL_CHAR_1;
5563       break;
5564
5565     case 0x15c8:        // (blue)
5566       element = EL_CHAR_2;
5567       break;
5568
5569     case 0x15c9:        // (blue)
5570       element = EL_CHAR_3;
5571       break;
5572
5573     case 0x15ca:        // (blue)
5574       element = EL_CHAR_4;
5575       break;
5576
5577     case 0x15cb:        // (blue)
5578       element = EL_CHAR_5;
5579       break;
5580
5581     case 0x15cc:        // (blue)
5582       element = EL_CHAR_6;
5583       break;
5584
5585     case 0x15cd:        // (blue)
5586       element = EL_CHAR_7;
5587       break;
5588
5589     case 0x15ce:        // (blue)
5590       element = EL_CHAR_8;
5591       break;
5592
5593     case 0x15cf:        // (blue)
5594       element = EL_CHAR_9;
5595       break;
5596
5597     case 0x15d0:        // (blue)
5598       element = EL_CHAR_PERIOD;
5599       break;
5600
5601     case 0x15d1:        // (blue)
5602       element = EL_CHAR_EXCLAM;
5603       break;
5604
5605     case 0x15d2:        // (blue)
5606       element = EL_CHAR_COLON;
5607       break;
5608
5609     case 0x15d3:        // (blue)
5610       element = EL_CHAR_LESS;
5611       break;
5612
5613     case 0x15d4:        // (blue)
5614       element = EL_CHAR_GREATER;
5615       break;
5616
5617     case 0x15d5:        // (blue)
5618       element = EL_CHAR_QUESTION;
5619       break;
5620
5621     case 0x15d6:        // (blue)
5622       element = EL_CHAR_COPYRIGHT;
5623       break;
5624
5625     case 0x15d7:        // (blue)
5626       element = EL_CHAR_UP;
5627       break;
5628
5629     case 0x15d8:        // (blue)
5630       element = EL_CHAR_DOWN;
5631       break;
5632
5633     case 0x15d9:        // (blue)
5634       element = EL_CHAR_BUTTON;
5635       break;
5636
5637     case 0x15da:        // (blue)
5638       element = EL_CHAR_PLUS;
5639       break;
5640
5641     case 0x15db:        // (blue)
5642       element = EL_CHAR_MINUS;
5643       break;
5644
5645     case 0x15dc:        // (blue)
5646       element = EL_CHAR_APOSTROPHE;
5647       break;
5648
5649     case 0x15dd:        // (blue)
5650       element = EL_CHAR_PARENLEFT;
5651       break;
5652
5653     case 0x15de:        // (blue)
5654       element = EL_CHAR_PARENRIGHT;
5655       break;
5656
5657     case 0x15df:        // (green)
5658       element = EL_CHAR_A;
5659       break;
5660
5661     case 0x15e0:        // (green)
5662       element = EL_CHAR_B;
5663       break;
5664
5665     case 0x15e1:        // (green)
5666       element = EL_CHAR_C;
5667       break;
5668
5669     case 0x15e2:        // (green)
5670       element = EL_CHAR_D;
5671       break;
5672
5673     case 0x15e3:        // (green)
5674       element = EL_CHAR_E;
5675       break;
5676
5677     case 0x15e4:        // (green)
5678       element = EL_CHAR_F;
5679       break;
5680
5681     case 0x15e5:        // (green)
5682       element = EL_CHAR_G;
5683       break;
5684
5685     case 0x15e6:        // (green)
5686       element = EL_CHAR_H;
5687       break;
5688
5689     case 0x15e7:        // (green)
5690       element = EL_CHAR_I;
5691       break;
5692
5693     case 0x15e8:        // (green)
5694       element = EL_CHAR_J;
5695       break;
5696
5697     case 0x15e9:        // (green)
5698       element = EL_CHAR_K;
5699       break;
5700
5701     case 0x15ea:        // (green)
5702       element = EL_CHAR_L;
5703       break;
5704
5705     case 0x15eb:        // (green)
5706       element = EL_CHAR_M;
5707       break;
5708
5709     case 0x15ec:        // (green)
5710       element = EL_CHAR_N;
5711       break;
5712
5713     case 0x15ed:        // (green)
5714       element = EL_CHAR_O;
5715       break;
5716
5717     case 0x15ee:        // (green)
5718       element = EL_CHAR_P;
5719       break;
5720
5721     case 0x15ef:        // (green)
5722       element = EL_CHAR_Q;
5723       break;
5724
5725     case 0x15f0:        // (green)
5726       element = EL_CHAR_R;
5727       break;
5728
5729     case 0x15f1:        // (green)
5730       element = EL_CHAR_S;
5731       break;
5732
5733     case 0x15f2:        // (green)
5734       element = EL_CHAR_T;
5735       break;
5736
5737     case 0x15f3:        // (green)
5738       element = EL_CHAR_U;
5739       break;
5740
5741     case 0x15f4:        // (green)
5742       element = EL_CHAR_V;
5743       break;
5744
5745     case 0x15f5:        // (green)
5746       element = EL_CHAR_W;
5747       break;
5748
5749     case 0x15f6:        // (green)
5750       element = EL_CHAR_X;
5751       break;
5752
5753     case 0x15f7:        // (green)
5754       element = EL_CHAR_Y;
5755       break;
5756
5757     case 0x15f8:        // (green)
5758       element = EL_CHAR_Z;
5759       break;
5760
5761     case 0x15f9:        // (green)
5762       element = EL_CHAR_AUMLAUT;
5763       break;
5764
5765     case 0x15fa:        // (green)
5766       element = EL_CHAR_OUMLAUT;
5767       break;
5768
5769     case 0x15fb:        // (green)
5770       element = EL_CHAR_UUMLAUT;
5771       break;
5772
5773     case 0x15fc:        // (green)
5774       element = EL_CHAR_0;
5775       break;
5776
5777     case 0x15fd:        // (green)
5778       element = EL_CHAR_1;
5779       break;
5780
5781     case 0x15fe:        // (green)
5782       element = EL_CHAR_2;
5783       break;
5784
5785     case 0x15ff:        // (green)
5786       element = EL_CHAR_3;
5787       break;
5788
5789     case 0x1600:        // (green)
5790       element = EL_CHAR_4;
5791       break;
5792
5793     case 0x1601:        // (green)
5794       element = EL_CHAR_5;
5795       break;
5796
5797     case 0x1602:        // (green)
5798       element = EL_CHAR_6;
5799       break;
5800
5801     case 0x1603:        // (green)
5802       element = EL_CHAR_7;
5803       break;
5804
5805     case 0x1604:        // (green)
5806       element = EL_CHAR_8;
5807       break;
5808
5809     case 0x1605:        // (green)
5810       element = EL_CHAR_9;
5811       break;
5812
5813     case 0x1606:        // (green)
5814       element = EL_CHAR_PERIOD;
5815       break;
5816
5817     case 0x1607:        // (green)
5818       element = EL_CHAR_EXCLAM;
5819       break;
5820
5821     case 0x1608:        // (green)
5822       element = EL_CHAR_COLON;
5823       break;
5824
5825     case 0x1609:        // (green)
5826       element = EL_CHAR_LESS;
5827       break;
5828
5829     case 0x160a:        // (green)
5830       element = EL_CHAR_GREATER;
5831       break;
5832
5833     case 0x160b:        // (green)
5834       element = EL_CHAR_QUESTION;
5835       break;
5836
5837     case 0x160c:        // (green)
5838       element = EL_CHAR_COPYRIGHT;
5839       break;
5840
5841     case 0x160d:        // (green)
5842       element = EL_CHAR_UP;
5843       break;
5844
5845     case 0x160e:        // (green)
5846       element = EL_CHAR_DOWN;
5847       break;
5848
5849     case 0x160f:        // (green)
5850       element = EL_CHAR_BUTTON;
5851       break;
5852
5853     case 0x1610:        // (green)
5854       element = EL_CHAR_PLUS;
5855       break;
5856
5857     case 0x1611:        // (green)
5858       element = EL_CHAR_MINUS;
5859       break;
5860
5861     case 0x1612:        // (green)
5862       element = EL_CHAR_APOSTROPHE;
5863       break;
5864
5865     case 0x1613:        // (green)
5866       element = EL_CHAR_PARENLEFT;
5867       break;
5868
5869     case 0x1614:        // (green)
5870       element = EL_CHAR_PARENRIGHT;
5871       break;
5872
5873     case 0x1615:        // (blue steel)
5874       element = EL_STEEL_CHAR_A;
5875       break;
5876
5877     case 0x1616:        // (blue steel)
5878       element = EL_STEEL_CHAR_B;
5879       break;
5880
5881     case 0x1617:        // (blue steel)
5882       element = EL_STEEL_CHAR_C;
5883       break;
5884
5885     case 0x1618:        // (blue steel)
5886       element = EL_STEEL_CHAR_D;
5887       break;
5888
5889     case 0x1619:        // (blue steel)
5890       element = EL_STEEL_CHAR_E;
5891       break;
5892
5893     case 0x161a:        // (blue steel)
5894       element = EL_STEEL_CHAR_F;
5895       break;
5896
5897     case 0x161b:        // (blue steel)
5898       element = EL_STEEL_CHAR_G;
5899       break;
5900
5901     case 0x161c:        // (blue steel)
5902       element = EL_STEEL_CHAR_H;
5903       break;
5904
5905     case 0x161d:        // (blue steel)
5906       element = EL_STEEL_CHAR_I;
5907       break;
5908
5909     case 0x161e:        // (blue steel)
5910       element = EL_STEEL_CHAR_J;
5911       break;
5912
5913     case 0x161f:        // (blue steel)
5914       element = EL_STEEL_CHAR_K;
5915       break;
5916
5917     case 0x1620:        // (blue steel)
5918       element = EL_STEEL_CHAR_L;
5919       break;
5920
5921     case 0x1621:        // (blue steel)
5922       element = EL_STEEL_CHAR_M;
5923       break;
5924
5925     case 0x1622:        // (blue steel)
5926       element = EL_STEEL_CHAR_N;
5927       break;
5928
5929     case 0x1623:        // (blue steel)
5930       element = EL_STEEL_CHAR_O;
5931       break;
5932
5933     case 0x1624:        // (blue steel)
5934       element = EL_STEEL_CHAR_P;
5935       break;
5936
5937     case 0x1625:        // (blue steel)
5938       element = EL_STEEL_CHAR_Q;
5939       break;
5940
5941     case 0x1626:        // (blue steel)
5942       element = EL_STEEL_CHAR_R;
5943       break;
5944
5945     case 0x1627:        // (blue steel)
5946       element = EL_STEEL_CHAR_S;
5947       break;
5948
5949     case 0x1628:        // (blue steel)
5950       element = EL_STEEL_CHAR_T;
5951       break;
5952
5953     case 0x1629:        // (blue steel)
5954       element = EL_STEEL_CHAR_U;
5955       break;
5956
5957     case 0x162a:        // (blue steel)
5958       element = EL_STEEL_CHAR_V;
5959       break;
5960
5961     case 0x162b:        // (blue steel)
5962       element = EL_STEEL_CHAR_W;
5963       break;
5964
5965     case 0x162c:        // (blue steel)
5966       element = EL_STEEL_CHAR_X;
5967       break;
5968
5969     case 0x162d:        // (blue steel)
5970       element = EL_STEEL_CHAR_Y;
5971       break;
5972
5973     case 0x162e:        // (blue steel)
5974       element = EL_STEEL_CHAR_Z;
5975       break;
5976
5977     case 0x162f:        // (blue steel)
5978       element = EL_STEEL_CHAR_AUMLAUT;
5979       break;
5980
5981     case 0x1630:        // (blue steel)
5982       element = EL_STEEL_CHAR_OUMLAUT;
5983       break;
5984
5985     case 0x1631:        // (blue steel)
5986       element = EL_STEEL_CHAR_UUMLAUT;
5987       break;
5988
5989     case 0x1632:        // (blue steel)
5990       element = EL_STEEL_CHAR_0;
5991       break;
5992
5993     case 0x1633:        // (blue steel)
5994       element = EL_STEEL_CHAR_1;
5995       break;
5996
5997     case 0x1634:        // (blue steel)
5998       element = EL_STEEL_CHAR_2;
5999       break;
6000
6001     case 0x1635:        // (blue steel)
6002       element = EL_STEEL_CHAR_3;
6003       break;
6004
6005     case 0x1636:        // (blue steel)
6006       element = EL_STEEL_CHAR_4;
6007       break;
6008
6009     case 0x1637:        // (blue steel)
6010       element = EL_STEEL_CHAR_5;
6011       break;
6012
6013     case 0x1638:        // (blue steel)
6014       element = EL_STEEL_CHAR_6;
6015       break;
6016
6017     case 0x1639:        // (blue steel)
6018       element = EL_STEEL_CHAR_7;
6019       break;
6020
6021     case 0x163a:        // (blue steel)
6022       element = EL_STEEL_CHAR_8;
6023       break;
6024
6025     case 0x163b:        // (blue steel)
6026       element = EL_STEEL_CHAR_9;
6027       break;
6028
6029     case 0x163c:        // (blue steel)
6030       element = EL_STEEL_CHAR_PERIOD;
6031       break;
6032
6033     case 0x163d:        // (blue steel)
6034       element = EL_STEEL_CHAR_EXCLAM;
6035       break;
6036
6037     case 0x163e:        // (blue steel)
6038       element = EL_STEEL_CHAR_COLON;
6039       break;
6040
6041     case 0x163f:        // (blue steel)
6042       element = EL_STEEL_CHAR_LESS;
6043       break;
6044
6045     case 0x1640:        // (blue steel)
6046       element = EL_STEEL_CHAR_GREATER;
6047       break;
6048
6049     case 0x1641:        // (blue steel)
6050       element = EL_STEEL_CHAR_QUESTION;
6051       break;
6052
6053     case 0x1642:        // (blue steel)
6054       element = EL_STEEL_CHAR_COPYRIGHT;
6055       break;
6056
6057     case 0x1643:        // (blue steel)
6058       element = EL_STEEL_CHAR_UP;
6059       break;
6060
6061     case 0x1644:        // (blue steel)
6062       element = EL_STEEL_CHAR_DOWN;
6063       break;
6064
6065     case 0x1645:        // (blue steel)
6066       element = EL_STEEL_CHAR_BUTTON;
6067       break;
6068
6069     case 0x1646:        // (blue steel)
6070       element = EL_STEEL_CHAR_PLUS;
6071       break;
6072
6073     case 0x1647:        // (blue steel)
6074       element = EL_STEEL_CHAR_MINUS;
6075       break;
6076
6077     case 0x1648:        // (blue steel)
6078       element = EL_STEEL_CHAR_APOSTROPHE;
6079       break;
6080
6081     case 0x1649:        // (blue steel)
6082       element = EL_STEEL_CHAR_PARENLEFT;
6083       break;
6084
6085     case 0x164a:        // (blue steel)
6086       element = EL_STEEL_CHAR_PARENRIGHT;
6087       break;
6088
6089     case 0x164b:        // (green steel)
6090       element = EL_STEEL_CHAR_A;
6091       break;
6092
6093     case 0x164c:        // (green steel)
6094       element = EL_STEEL_CHAR_B;
6095       break;
6096
6097     case 0x164d:        // (green steel)
6098       element = EL_STEEL_CHAR_C;
6099       break;
6100
6101     case 0x164e:        // (green steel)
6102       element = EL_STEEL_CHAR_D;
6103       break;
6104
6105     case 0x164f:        // (green steel)
6106       element = EL_STEEL_CHAR_E;
6107       break;
6108
6109     case 0x1650:        // (green steel)
6110       element = EL_STEEL_CHAR_F;
6111       break;
6112
6113     case 0x1651:        // (green steel)
6114       element = EL_STEEL_CHAR_G;
6115       break;
6116
6117     case 0x1652:        // (green steel)
6118       element = EL_STEEL_CHAR_H;
6119       break;
6120
6121     case 0x1653:        // (green steel)
6122       element = EL_STEEL_CHAR_I;
6123       break;
6124
6125     case 0x1654:        // (green steel)
6126       element = EL_STEEL_CHAR_J;
6127       break;
6128
6129     case 0x1655:        // (green steel)
6130       element = EL_STEEL_CHAR_K;
6131       break;
6132
6133     case 0x1656:        // (green steel)
6134       element = EL_STEEL_CHAR_L;
6135       break;
6136
6137     case 0x1657:        // (green steel)
6138       element = EL_STEEL_CHAR_M;
6139       break;
6140
6141     case 0x1658:        // (green steel)
6142       element = EL_STEEL_CHAR_N;
6143       break;
6144
6145     case 0x1659:        // (green steel)
6146       element = EL_STEEL_CHAR_O;
6147       break;
6148
6149     case 0x165a:        // (green steel)
6150       element = EL_STEEL_CHAR_P;
6151       break;
6152
6153     case 0x165b:        // (green steel)
6154       element = EL_STEEL_CHAR_Q;
6155       break;
6156
6157     case 0x165c:        // (green steel)
6158       element = EL_STEEL_CHAR_R;
6159       break;
6160
6161     case 0x165d:        // (green steel)
6162       element = EL_STEEL_CHAR_S;
6163       break;
6164
6165     case 0x165e:        // (green steel)
6166       element = EL_STEEL_CHAR_T;
6167       break;
6168
6169     case 0x165f:        // (green steel)
6170       element = EL_STEEL_CHAR_U;
6171       break;
6172
6173     case 0x1660:        // (green steel)
6174       element = EL_STEEL_CHAR_V;
6175       break;
6176
6177     case 0x1661:        // (green steel)
6178       element = EL_STEEL_CHAR_W;
6179       break;
6180
6181     case 0x1662:        // (green steel)
6182       element = EL_STEEL_CHAR_X;
6183       break;
6184
6185     case 0x1663:        // (green steel)
6186       element = EL_STEEL_CHAR_Y;
6187       break;
6188
6189     case 0x1664:        // (green steel)
6190       element = EL_STEEL_CHAR_Z;
6191       break;
6192
6193     case 0x1665:        // (green steel)
6194       element = EL_STEEL_CHAR_AUMLAUT;
6195       break;
6196
6197     case 0x1666:        // (green steel)
6198       element = EL_STEEL_CHAR_OUMLAUT;
6199       break;
6200
6201     case 0x1667:        // (green steel)
6202       element = EL_STEEL_CHAR_UUMLAUT;
6203       break;
6204
6205     case 0x1668:        // (green steel)
6206       element = EL_STEEL_CHAR_0;
6207       break;
6208
6209     case 0x1669:        // (green steel)
6210       element = EL_STEEL_CHAR_1;
6211       break;
6212
6213     case 0x166a:        // (green steel)
6214       element = EL_STEEL_CHAR_2;
6215       break;
6216
6217     case 0x166b:        // (green steel)
6218       element = EL_STEEL_CHAR_3;
6219       break;
6220
6221     case 0x166c:        // (green steel)
6222       element = EL_STEEL_CHAR_4;
6223       break;
6224
6225     case 0x166d:        // (green steel)
6226       element = EL_STEEL_CHAR_5;
6227       break;
6228
6229     case 0x166e:        // (green steel)
6230       element = EL_STEEL_CHAR_6;
6231       break;
6232
6233     case 0x166f:        // (green steel)
6234       element = EL_STEEL_CHAR_7;
6235       break;
6236
6237     case 0x1670:        // (green steel)
6238       element = EL_STEEL_CHAR_8;
6239       break;
6240
6241     case 0x1671:        // (green steel)
6242       element = EL_STEEL_CHAR_9;
6243       break;
6244
6245     case 0x1672:        // (green steel)
6246       element = EL_STEEL_CHAR_PERIOD;
6247       break;
6248
6249     case 0x1673:        // (green steel)
6250       element = EL_STEEL_CHAR_EXCLAM;
6251       break;
6252
6253     case 0x1674:        // (green steel)
6254       element = EL_STEEL_CHAR_COLON;
6255       break;
6256
6257     case 0x1675:        // (green steel)
6258       element = EL_STEEL_CHAR_LESS;
6259       break;
6260
6261     case 0x1676:        // (green steel)
6262       element = EL_STEEL_CHAR_GREATER;
6263       break;
6264
6265     case 0x1677:        // (green steel)
6266       element = EL_STEEL_CHAR_QUESTION;
6267       break;
6268
6269     case 0x1678:        // (green steel)
6270       element = EL_STEEL_CHAR_COPYRIGHT;
6271       break;
6272
6273     case 0x1679:        // (green steel)
6274       element = EL_STEEL_CHAR_UP;
6275       break;
6276
6277     case 0x167a:        // (green steel)
6278       element = EL_STEEL_CHAR_DOWN;
6279       break;
6280
6281     case 0x167b:        // (green steel)
6282       element = EL_STEEL_CHAR_BUTTON;
6283       break;
6284
6285     case 0x167c:        // (green steel)
6286       element = EL_STEEL_CHAR_PLUS;
6287       break;
6288
6289     case 0x167d:        // (green steel)
6290       element = EL_STEEL_CHAR_MINUS;
6291       break;
6292
6293     case 0x167e:        // (green steel)
6294       element = EL_STEEL_CHAR_APOSTROPHE;
6295       break;
6296
6297     case 0x167f:        // (green steel)
6298       element = EL_STEEL_CHAR_PARENLEFT;
6299       break;
6300
6301     case 0x1680:        // (green steel)
6302       element = EL_STEEL_CHAR_PARENRIGHT;
6303       break;
6304
6305     case 0x1681:        // gate (red)
6306       element = EL_EM_GATE_1;
6307       break;
6308
6309     case 0x1682:        // secret gate (red)
6310       element = EL_EM_GATE_1_GRAY;
6311       break;
6312
6313     case 0x1683:        // gate (yellow)
6314       element = EL_EM_GATE_2;
6315       break;
6316
6317     case 0x1684:        // secret gate (yellow)
6318       element = EL_EM_GATE_2_GRAY;
6319       break;
6320
6321     case 0x1685:        // gate (blue)
6322       element = EL_EM_GATE_4;
6323       break;
6324
6325     case 0x1686:        // secret gate (blue)
6326       element = EL_EM_GATE_4_GRAY;
6327       break;
6328
6329     case 0x1687:        // gate (green)
6330       element = EL_EM_GATE_3;
6331       break;
6332
6333     case 0x1688:        // secret gate (green)
6334       element = EL_EM_GATE_3_GRAY;
6335       break;
6336
6337     case 0x1689:        // gate (white)
6338       element = EL_DC_GATE_WHITE;
6339       break;
6340
6341     case 0x168a:        // secret gate (white)
6342       element = EL_DC_GATE_WHITE_GRAY;
6343       break;
6344
6345     case 0x168b:        // secret gate (no key)
6346       element = EL_DC_GATE_FAKE_GRAY;
6347       break;
6348
6349     case 0x168c:
6350       element = EL_ROBOT_WHEEL;
6351       break;
6352
6353     case 0x168d:
6354       element = EL_DC_TIMEGATE_SWITCH;
6355       break;
6356
6357     case 0x168e:
6358       element = EL_ACID_POOL_BOTTOM;
6359       break;
6360
6361     case 0x168f:
6362       element = EL_ACID_POOL_TOPLEFT;
6363       break;
6364
6365     case 0x1690:
6366       element = EL_ACID_POOL_TOPRIGHT;
6367       break;
6368
6369     case 0x1691:
6370       element = EL_ACID_POOL_BOTTOMLEFT;
6371       break;
6372
6373     case 0x1692:
6374       element = EL_ACID_POOL_BOTTOMRIGHT;
6375       break;
6376
6377     case 0x1693:
6378       element = EL_STEELWALL;
6379       break;
6380
6381     case 0x1694:
6382       element = EL_STEELWALL_SLIPPERY;
6383       break;
6384
6385     case 0x1695:        // steel wall (not round)
6386       element = EL_STEELWALL;
6387       break;
6388
6389     case 0x1696:        // steel wall (left)
6390       element = EL_DC_STEELWALL_1_LEFT;
6391       break;
6392
6393     case 0x1697:        // steel wall (bottom)
6394       element = EL_DC_STEELWALL_1_BOTTOM;
6395       break;
6396
6397     case 0x1698:        // steel wall (right)
6398       element = EL_DC_STEELWALL_1_RIGHT;
6399       break;
6400
6401     case 0x1699:        // steel wall (top)
6402       element = EL_DC_STEELWALL_1_TOP;
6403       break;
6404
6405     case 0x169a:        // steel wall (left/bottom)
6406       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6407       break;
6408
6409     case 0x169b:        // steel wall (right/bottom)
6410       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6411       break;
6412
6413     case 0x169c:        // steel wall (right/top)
6414       element = EL_DC_STEELWALL_1_TOPRIGHT;
6415       break;
6416
6417     case 0x169d:        // steel wall (left/top)
6418       element = EL_DC_STEELWALL_1_TOPLEFT;
6419       break;
6420
6421     case 0x169e:        // steel wall (right/bottom small)
6422       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6423       break;
6424
6425     case 0x169f:        // steel wall (left/bottom small)
6426       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6427       break;
6428
6429     case 0x16a0:        // steel wall (right/top small)
6430       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6431       break;
6432
6433     case 0x16a1:        // steel wall (left/top small)
6434       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6435       break;
6436
6437     case 0x16a2:        // steel wall (left/right)
6438       element = EL_DC_STEELWALL_1_VERTICAL;
6439       break;
6440
6441     case 0x16a3:        // steel wall (top/bottom)
6442       element = EL_DC_STEELWALL_1_HORIZONTAL;
6443       break;
6444
6445     case 0x16a4:        // steel wall 2 (left end)
6446       element = EL_DC_STEELWALL_2_LEFT;
6447       break;
6448
6449     case 0x16a5:        // steel wall 2 (right end)
6450       element = EL_DC_STEELWALL_2_RIGHT;
6451       break;
6452
6453     case 0x16a6:        // steel wall 2 (top end)
6454       element = EL_DC_STEELWALL_2_TOP;
6455       break;
6456
6457     case 0x16a7:        // steel wall 2 (bottom end)
6458       element = EL_DC_STEELWALL_2_BOTTOM;
6459       break;
6460
6461     case 0x16a8:        // steel wall 2 (left/right)
6462       element = EL_DC_STEELWALL_2_HORIZONTAL;
6463       break;
6464
6465     case 0x16a9:        // steel wall 2 (up/down)
6466       element = EL_DC_STEELWALL_2_VERTICAL;
6467       break;
6468
6469     case 0x16aa:        // steel wall 2 (mid)
6470       element = EL_DC_STEELWALL_2_MIDDLE;
6471       break;
6472
6473     case 0x16ab:
6474       element = EL_SIGN_EXCLAMATION;
6475       break;
6476
6477     case 0x16ac:
6478       element = EL_SIGN_RADIOACTIVITY;
6479       break;
6480
6481     case 0x16ad:
6482       element = EL_SIGN_STOP;
6483       break;
6484
6485     case 0x16ae:
6486       element = EL_SIGN_WHEELCHAIR;
6487       break;
6488
6489     case 0x16af:
6490       element = EL_SIGN_PARKING;
6491       break;
6492
6493     case 0x16b0:
6494       element = EL_SIGN_NO_ENTRY;
6495       break;
6496
6497     case 0x16b1:
6498       element = EL_SIGN_HEART;
6499       break;
6500
6501     case 0x16b2:
6502       element = EL_SIGN_GIVE_WAY;
6503       break;
6504
6505     case 0x16b3:
6506       element = EL_SIGN_ENTRY_FORBIDDEN;
6507       break;
6508
6509     case 0x16b4:
6510       element = EL_SIGN_EMERGENCY_EXIT;
6511       break;
6512
6513     case 0x16b5:
6514       element = EL_SIGN_YIN_YANG;
6515       break;
6516
6517     case 0x16b6:
6518       element = EL_WALL_EMERALD;
6519       break;
6520
6521     case 0x16b7:
6522       element = EL_WALL_DIAMOND;
6523       break;
6524
6525     case 0x16b8:
6526       element = EL_WALL_PEARL;
6527       break;
6528
6529     case 0x16b9:
6530       element = EL_WALL_CRYSTAL;
6531       break;
6532
6533     case 0x16ba:
6534       element = EL_INVISIBLE_WALL;
6535       break;
6536
6537     case 0x16bb:
6538       element = EL_INVISIBLE_STEELWALL;
6539       break;
6540
6541       // 0x16bc - 0x16cb:
6542       // EL_INVISIBLE_SAND
6543
6544     case 0x16cc:
6545       element = EL_LIGHT_SWITCH;
6546       break;
6547
6548     case 0x16cd:
6549       element = EL_ENVELOPE_1;
6550       break;
6551
6552     default:
6553       if (element >= 0x0117 && element <= 0x036e)       // (?)
6554         element = EL_DIAMOND;
6555       else if (element >= 0x042d && element <= 0x0684)  // (?)
6556         element = EL_EMERALD;
6557       else if (element >= 0x157c && element <= 0x158b)
6558         element = EL_SAND;
6559       else if (element >= 0x1590 && element <= 0x159f)
6560         element = EL_DC_LANDMINE;
6561       else if (element >= 0x16bc && element <= 0x16cb)
6562         element = EL_INVISIBLE_SAND;
6563       else
6564       {
6565         Warn("unknown Diamond Caves element 0x%04x", element);
6566
6567         element = EL_UNKNOWN;
6568       }
6569       break;
6570   }
6571
6572   return getMappedElement(element);
6573 }
6574
6575 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6576 {
6577   byte header[DC_LEVEL_HEADER_SIZE];
6578   int envelope_size;
6579   int envelope_header_pos = 62;
6580   int envelope_content_pos = 94;
6581   int level_name_pos = 251;
6582   int level_author_pos = 292;
6583   int envelope_header_len;
6584   int envelope_content_len;
6585   int level_name_len;
6586   int level_author_len;
6587   int fieldx, fieldy;
6588   int num_yamyam_contents;
6589   int i, x, y;
6590
6591   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6592
6593   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6594   {
6595     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6596
6597     header[i * 2 + 0] = header_word >> 8;
6598     header[i * 2 + 1] = header_word & 0xff;
6599   }
6600
6601   // read some values from level header to check level decoding integrity
6602   fieldx = header[6] | (header[7] << 8);
6603   fieldy = header[8] | (header[9] << 8);
6604   num_yamyam_contents = header[60] | (header[61] << 8);
6605
6606   // do some simple sanity checks to ensure that level was correctly decoded
6607   if (fieldx < 1 || fieldx > 256 ||
6608       fieldy < 1 || fieldy > 256 ||
6609       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6610   {
6611     level->no_valid_file = TRUE;
6612
6613     Warn("cannot decode level from stream -- using empty level");
6614
6615     return;
6616   }
6617
6618   // maximum envelope header size is 31 bytes
6619   envelope_header_len   = header[envelope_header_pos];
6620   // maximum envelope content size is 110 (156?) bytes
6621   envelope_content_len  = header[envelope_content_pos];
6622
6623   // maximum level title size is 40 bytes
6624   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6625   // maximum level author size is 30 (51?) bytes
6626   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6627
6628   envelope_size = 0;
6629
6630   for (i = 0; i < envelope_header_len; i++)
6631     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6632       level->envelope[0].text[envelope_size++] =
6633         header[envelope_header_pos + 1 + i];
6634
6635   if (envelope_header_len > 0 && envelope_content_len > 0)
6636   {
6637     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6638       level->envelope[0].text[envelope_size++] = '\n';
6639     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6640       level->envelope[0].text[envelope_size++] = '\n';
6641   }
6642
6643   for (i = 0; i < envelope_content_len; i++)
6644     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6645       level->envelope[0].text[envelope_size++] =
6646         header[envelope_content_pos + 1 + i];
6647
6648   level->envelope[0].text[envelope_size] = '\0';
6649
6650   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6651   level->envelope[0].ysize = 10;
6652   level->envelope[0].autowrap = TRUE;
6653   level->envelope[0].centered = TRUE;
6654
6655   for (i = 0; i < level_name_len; i++)
6656     level->name[i] = header[level_name_pos + 1 + i];
6657   level->name[level_name_len] = '\0';
6658
6659   for (i = 0; i < level_author_len; i++)
6660     level->author[i] = header[level_author_pos + 1 + i];
6661   level->author[level_author_len] = '\0';
6662
6663   num_yamyam_contents = header[60] | (header[61] << 8);
6664   level->num_yamyam_contents =
6665     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6666
6667   for (i = 0; i < num_yamyam_contents; i++)
6668   {
6669     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6670     {
6671       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6672       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6673
6674       if (i < MAX_ELEMENT_CONTENTS)
6675         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6676     }
6677   }
6678
6679   fieldx = header[6] | (header[7] << 8);
6680   fieldy = header[8] | (header[9] << 8);
6681   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6682   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6683
6684   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6685   {
6686     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6687     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6688
6689     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6690       level->field[x][y] = getMappedElement_DC(element_dc);
6691   }
6692
6693   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6694   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6695   level->field[x][y] = EL_PLAYER_1;
6696
6697   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6698   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6699   level->field[x][y] = EL_PLAYER_2;
6700
6701   level->gems_needed            = header[18] | (header[19] << 8);
6702
6703   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6704   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6705   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6706   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6707   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6708   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6709   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6710   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6711   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6712   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6713   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6714   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6715
6716   level->time                   = header[44] | (header[45] << 8);
6717
6718   level->amoeba_speed           = header[46] | (header[47] << 8);
6719   level->time_light             = header[48] | (header[49] << 8);
6720   level->time_timegate          = header[50] | (header[51] << 8);
6721   level->time_wheel             = header[52] | (header[53] << 8);
6722   level->time_magic_wall        = header[54] | (header[55] << 8);
6723   level->extra_time             = header[56] | (header[57] << 8);
6724   level->shield_normal_time     = header[58] | (header[59] << 8);
6725
6726   // shield and extra time elements do not have a score
6727   level->score[SC_SHIELD]       = 0;
6728   level->extra_time_score       = 0;
6729
6730   // set time for normal and deadly shields to the same value
6731   level->shield_deadly_time     = level->shield_normal_time;
6732
6733   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6734   // can slip down from flat walls, like normal walls and steel walls
6735   level->em_slippery_gems = TRUE;
6736
6737   // time score is counted for each 10 seconds left in Diamond Caves levels
6738   level->time_score_base = 10;
6739 }
6740
6741 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6742                                      struct LevelFileInfo *level_file_info,
6743                                      boolean level_info_only)
6744 {
6745   char *filename = level_file_info->filename;
6746   File *file;
6747   int num_magic_bytes = 8;
6748   char magic_bytes[num_magic_bytes + 1];
6749   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6750
6751   if (!(file = openFile(filename, MODE_READ)))
6752   {
6753     level->no_valid_file = TRUE;
6754
6755     if (!level_info_only)
6756       Warn("cannot read level '%s' -- using empty level", filename);
6757
6758     return;
6759   }
6760
6761   // fseek(file, 0x0000, SEEK_SET);
6762
6763   if (level_file_info->packed)
6764   {
6765     // read "magic bytes" from start of file
6766     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6767       magic_bytes[0] = '\0';
6768
6769     // check "magic bytes" for correct file format
6770     if (!strPrefix(magic_bytes, "DC2"))
6771     {
6772       level->no_valid_file = TRUE;
6773
6774       Warn("unknown DC level file '%s' -- using empty level", filename);
6775
6776       return;
6777     }
6778
6779     if (strPrefix(magic_bytes, "DC2Win95") ||
6780         strPrefix(magic_bytes, "DC2Win98"))
6781     {
6782       int position_first_level = 0x00fa;
6783       int extra_bytes = 4;
6784       int skip_bytes;
6785
6786       // advance file stream to first level inside the level package
6787       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6788
6789       // each block of level data is followed by block of non-level data
6790       num_levels_to_skip *= 2;
6791
6792       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6793       while (num_levels_to_skip >= 0)
6794       {
6795         // advance file stream to next level inside the level package
6796         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6797         {
6798           level->no_valid_file = TRUE;
6799
6800           Warn("cannot fseek in file '%s' -- using empty level", filename);
6801
6802           return;
6803         }
6804
6805         // skip apparently unused extra bytes following each level
6806         ReadUnusedBytesFromFile(file, extra_bytes);
6807
6808         // read size of next level in level package
6809         skip_bytes = getFile32BitLE(file);
6810
6811         num_levels_to_skip--;
6812       }
6813     }
6814     else
6815     {
6816       level->no_valid_file = TRUE;
6817
6818       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6819
6820       return;
6821     }
6822   }
6823
6824   LoadLevelFromFileStream_DC(file, level);
6825
6826   closeFile(file);
6827 }
6828
6829
6830 // ----------------------------------------------------------------------------
6831 // functions for loading SB level
6832 // ----------------------------------------------------------------------------
6833
6834 int getMappedElement_SB(int element_ascii, boolean use_ces)
6835 {
6836   static struct
6837   {
6838     int ascii;
6839     int sb;
6840     int ce;
6841   }
6842   sb_element_mapping[] =
6843   {
6844     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6845     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6846     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6847     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6848     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6849     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6850     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6851     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6852
6853     { 0,   -1,                      -1          },
6854   };
6855
6856   int i;
6857
6858   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6859     if (element_ascii == sb_element_mapping[i].ascii)
6860       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6861
6862   return EL_UNDEFINED;
6863 }
6864
6865 static void SetLevelSettings_SB(struct LevelInfo *level)
6866 {
6867   // time settings
6868   level->time = 0;
6869   level->use_step_counter = TRUE;
6870
6871   // score settings
6872   level->score[SC_TIME_BONUS] = 0;
6873   level->time_score_base = 1;
6874   level->rate_time_over_score = TRUE;
6875
6876   // game settings
6877   level->auto_exit_sokoban = TRUE;
6878 }
6879
6880 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6881                                      struct LevelFileInfo *level_file_info,
6882                                      boolean level_info_only)
6883 {
6884   char *filename = level_file_info->filename;
6885   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6886   char last_comment[MAX_LINE_LEN];
6887   char level_name[MAX_LINE_LEN];
6888   char *line_ptr;
6889   File *file;
6890   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6891   boolean read_continued_line = FALSE;
6892   boolean reading_playfield = FALSE;
6893   boolean got_valid_playfield_line = FALSE;
6894   boolean invalid_playfield_char = FALSE;
6895   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6896   int file_level_nr = 0;
6897   int x = 0, y = 0;             // initialized to make compilers happy
6898
6899   last_comment[0] = '\0';
6900   level_name[0] = '\0';
6901
6902   if (!(file = openFile(filename, MODE_READ)))
6903   {
6904     level->no_valid_file = TRUE;
6905
6906     if (!level_info_only)
6907       Warn("cannot read level '%s' -- using empty level", filename);
6908
6909     return;
6910   }
6911
6912   while (!checkEndOfFile(file))
6913   {
6914     // level successfully read, but next level may follow here
6915     if (!got_valid_playfield_line && reading_playfield)
6916     {
6917       // read playfield from single level file -- skip remaining file
6918       if (!level_file_info->packed)
6919         break;
6920
6921       if (file_level_nr >= num_levels_to_skip)
6922         break;
6923
6924       file_level_nr++;
6925
6926       last_comment[0] = '\0';
6927       level_name[0] = '\0';
6928
6929       reading_playfield = FALSE;
6930     }
6931
6932     got_valid_playfield_line = FALSE;
6933
6934     // read next line of input file
6935     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6936       break;
6937
6938     // cut trailing line break (this can be newline and/or carriage return)
6939     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6940       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6941         *line_ptr = '\0';
6942
6943     // copy raw input line for later use (mainly debugging output)
6944     strcpy(line_raw, line);
6945
6946     if (read_continued_line)
6947     {
6948       // append new line to existing line, if there is enough space
6949       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6950         strcat(previous_line, line_ptr);
6951
6952       strcpy(line, previous_line);      // copy storage buffer to line
6953
6954       read_continued_line = FALSE;
6955     }
6956
6957     // if the last character is '\', continue at next line
6958     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6959     {
6960       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6961       strcpy(previous_line, line);      // copy line to storage buffer
6962
6963       read_continued_line = TRUE;
6964
6965       continue;
6966     }
6967
6968     // skip empty lines
6969     if (line[0] == '\0')
6970       continue;
6971
6972     // extract comment text from comment line
6973     if (line[0] == ';')
6974     {
6975       for (line_ptr = line; *line_ptr; line_ptr++)
6976         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6977           break;
6978
6979       strcpy(last_comment, line_ptr);
6980
6981       continue;
6982     }
6983
6984     // extract level title text from line containing level title
6985     if (line[0] == '\'')
6986     {
6987       strcpy(level_name, &line[1]);
6988
6989       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6990         level_name[strlen(level_name) - 1] = '\0';
6991
6992       continue;
6993     }
6994
6995     // skip lines containing only spaces (or empty lines)
6996     for (line_ptr = line; *line_ptr; line_ptr++)
6997       if (*line_ptr != ' ')
6998         break;
6999     if (*line_ptr == '\0')
7000       continue;
7001
7002     // at this point, we have found a line containing part of a playfield
7003
7004     got_valid_playfield_line = TRUE;
7005
7006     if (!reading_playfield)
7007     {
7008       reading_playfield = TRUE;
7009       invalid_playfield_char = FALSE;
7010
7011       for (x = 0; x < MAX_LEV_FIELDX; x++)
7012         for (y = 0; y < MAX_LEV_FIELDY; y++)
7013           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7014
7015       level->fieldx = 0;
7016       level->fieldy = 0;
7017
7018       // start with topmost tile row
7019       y = 0;
7020     }
7021
7022     // skip playfield line if larger row than allowed
7023     if (y >= MAX_LEV_FIELDY)
7024       continue;
7025
7026     // start with leftmost tile column
7027     x = 0;
7028
7029     // read playfield elements from line
7030     for (line_ptr = line; *line_ptr; line_ptr++)
7031     {
7032       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7033
7034       // stop parsing playfield line if larger column than allowed
7035       if (x >= MAX_LEV_FIELDX)
7036         break;
7037
7038       if (mapped_sb_element == EL_UNDEFINED)
7039       {
7040         invalid_playfield_char = TRUE;
7041
7042         break;
7043       }
7044
7045       level->field[x][y] = mapped_sb_element;
7046
7047       // continue with next tile column
7048       x++;
7049
7050       level->fieldx = MAX(x, level->fieldx);
7051     }
7052
7053     if (invalid_playfield_char)
7054     {
7055       // if first playfield line, treat invalid lines as comment lines
7056       if (y == 0)
7057         reading_playfield = FALSE;
7058
7059       continue;
7060     }
7061
7062     // continue with next tile row
7063     y++;
7064   }
7065
7066   closeFile(file);
7067
7068   level->fieldy = y;
7069
7070   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7071   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7072
7073   if (!reading_playfield)
7074   {
7075     level->no_valid_file = TRUE;
7076
7077     Warn("cannot read level '%s' -- using empty level", filename);
7078
7079     return;
7080   }
7081
7082   if (*level_name != '\0')
7083   {
7084     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7085     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7086   }
7087   else if (*last_comment != '\0')
7088   {
7089     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7090     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7091   }
7092   else
7093   {
7094     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7095   }
7096
7097   // set all empty fields beyond the border walls to invisible steel wall
7098   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7099   {
7100     if ((x == 0 || x == level->fieldx - 1 ||
7101          y == 0 || y == level->fieldy - 1) &&
7102         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7103       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7104                      level->field, level->fieldx, level->fieldy);
7105   }
7106
7107   // set special level settings for Sokoban levels
7108   SetLevelSettings_SB(level);
7109
7110   if (load_xsb_to_ces)
7111   {
7112     // special global settings can now be set in level template
7113     level->use_custom_template = TRUE;
7114   }
7115 }
7116
7117
7118 // -------------------------------------------------------------------------
7119 // functions for handling native levels
7120 // -------------------------------------------------------------------------
7121
7122 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7123                                      struct LevelFileInfo *level_file_info,
7124                                      boolean level_info_only)
7125 {
7126   int pos = 0;
7127
7128   // determine position of requested level inside level package
7129   if (level_file_info->packed)
7130     pos = level_file_info->nr - leveldir_current->first_level;
7131
7132   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7133     level->no_valid_file = TRUE;
7134 }
7135
7136 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7137                                      struct LevelFileInfo *level_file_info,
7138                                      boolean level_info_only)
7139 {
7140   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7141     level->no_valid_file = TRUE;
7142 }
7143
7144 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7145                                      struct LevelFileInfo *level_file_info,
7146                                      boolean level_info_only)
7147 {
7148   int pos = 0;
7149
7150   // determine position of requested level inside level package
7151   if (level_file_info->packed)
7152     pos = level_file_info->nr - leveldir_current->first_level;
7153
7154   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7155     level->no_valid_file = TRUE;
7156 }
7157
7158 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7159                                      struct LevelFileInfo *level_file_info,
7160                                      boolean level_info_only)
7161 {
7162   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7163     level->no_valid_file = TRUE;
7164 }
7165
7166 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7167 {
7168   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7169     CopyNativeLevel_RND_to_BD(level);
7170   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7171     CopyNativeLevel_RND_to_EM(level);
7172   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7173     CopyNativeLevel_RND_to_SP(level);
7174   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7175     CopyNativeLevel_RND_to_MM(level);
7176 }
7177
7178 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7179 {
7180   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7181     CopyNativeLevel_BD_to_RND(level);
7182   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7183     CopyNativeLevel_EM_to_RND(level);
7184   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7185     CopyNativeLevel_SP_to_RND(level);
7186   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7187     CopyNativeLevel_MM_to_RND(level);
7188 }
7189
7190 void SaveNativeLevel(struct LevelInfo *level)
7191 {
7192   // saving native level files only supported for some game engines
7193   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7194       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7195     return;
7196
7197   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7198                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7199   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7200   char *filename = getLevelFilenameFromBasename(basename);
7201
7202   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7203     return;
7204
7205   boolean success = FALSE;
7206
7207   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7208   {
7209     CopyNativeLevel_RND_to_BD(level);
7210     // CopyNativeTape_RND_to_BD(level);
7211
7212     success = SaveNativeLevel_BD(filename);
7213   }
7214   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7215   {
7216     CopyNativeLevel_RND_to_SP(level);
7217     CopyNativeTape_RND_to_SP(level);
7218
7219     success = SaveNativeLevel_SP(filename);
7220   }
7221
7222   if (success)
7223     Request("Native level file saved!", REQ_CONFIRM);
7224   else
7225     Request("Failed to save native level file!", REQ_CONFIRM);
7226 }
7227
7228
7229 // ----------------------------------------------------------------------------
7230 // functions for loading generic level
7231 // ----------------------------------------------------------------------------
7232
7233 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7234                                   struct LevelFileInfo *level_file_info,
7235                                   boolean level_info_only)
7236 {
7237   // always start with reliable default values
7238   setLevelInfoToDefaults(level, level_info_only, TRUE);
7239
7240   switch (level_file_info->type)
7241   {
7242     case LEVEL_FILE_TYPE_RND:
7243       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7244       break;
7245
7246     case LEVEL_FILE_TYPE_BD:
7247       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7248       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7249       break;
7250
7251     case LEVEL_FILE_TYPE_EM:
7252       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7253       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7254       break;
7255
7256     case LEVEL_FILE_TYPE_SP:
7257       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7258       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7259       break;
7260
7261     case LEVEL_FILE_TYPE_MM:
7262       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7263       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7264       break;
7265
7266     case LEVEL_FILE_TYPE_DC:
7267       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7268       break;
7269
7270     case LEVEL_FILE_TYPE_SB:
7271       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7272       break;
7273
7274     default:
7275       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7276       break;
7277   }
7278
7279   // if level file is invalid, restore level structure to default values
7280   if (level->no_valid_file)
7281     setLevelInfoToDefaults(level, level_info_only, FALSE);
7282
7283   if (check_special_flags("use_native_bd_game_engine"))
7284     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7285
7286   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7287     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7288
7289   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7290     CopyNativeLevel_Native_to_RND(level);
7291 }
7292
7293 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7294 {
7295   static struct LevelFileInfo level_file_info;
7296
7297   // always start with reliable default values
7298   setFileInfoToDefaults(&level_file_info);
7299
7300   level_file_info.nr = 0;                       // unknown level number
7301   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7302
7303   setString(&level_file_info.filename, filename);
7304
7305   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7306 }
7307
7308 static void LoadLevel_InitVersion(struct LevelInfo *level)
7309 {
7310   int i, j;
7311
7312   if (leveldir_current == NULL)         // only when dumping level
7313     return;
7314
7315   // all engine modifications also valid for levels which use latest engine
7316   if (level->game_version < VERSION_IDENT(3,2,0,5))
7317   {
7318     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7319     level->time_score_base = 10;
7320   }
7321
7322   if (leveldir_current->latest_engine)
7323   {
7324     // ---------- use latest game engine --------------------------------------
7325
7326     /* For all levels which are forced to use the latest game engine version
7327        (normally all but user contributed, private and undefined levels), set
7328        the game engine version to the actual version; this allows for actual
7329        corrections in the game engine to take effect for existing, converted
7330        levels (from "classic" or other existing games) to make the emulation
7331        of the corresponding game more accurate, while (hopefully) not breaking
7332        existing levels created from other players. */
7333
7334     level->game_version = GAME_VERSION_ACTUAL;
7335
7336     /* Set special EM style gems behaviour: EM style gems slip down from
7337        normal, steel and growing wall. As this is a more fundamental change,
7338        it seems better to set the default behaviour to "off" (as it is more
7339        natural) and make it configurable in the level editor (as a property
7340        of gem style elements). Already existing converted levels (neither
7341        private nor contributed levels) are changed to the new behaviour. */
7342
7343     if (level->file_version < FILE_VERSION_2_0)
7344       level->em_slippery_gems = TRUE;
7345
7346     return;
7347   }
7348
7349   // ---------- use game engine the level was created with --------------------
7350
7351   /* For all levels which are not forced to use the latest game engine
7352      version (normally user contributed, private and undefined levels),
7353      use the version of the game engine the levels were created for.
7354
7355      Since 2.0.1, the game engine version is now directly stored
7356      in the level file (chunk "VERS"), so there is no need anymore
7357      to set the game version from the file version (except for old,
7358      pre-2.0 levels, where the game version is still taken from the
7359      file format version used to store the level -- see above). */
7360
7361   // player was faster than enemies in 1.0.0 and before
7362   if (level->file_version == FILE_VERSION_1_0)
7363     for (i = 0; i < MAX_PLAYERS; i++)
7364       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7365
7366   // default behaviour for EM style gems was "slippery" only in 2.0.1
7367   if (level->game_version == VERSION_IDENT(2,0,1,0))
7368     level->em_slippery_gems = TRUE;
7369
7370   // springs could be pushed over pits before (pre-release version) 2.2.0
7371   if (level->game_version < VERSION_IDENT(2,2,0,0))
7372     level->use_spring_bug = TRUE;
7373
7374   if (level->game_version < VERSION_IDENT(3,2,0,5))
7375   {
7376     // time orb caused limited time in endless time levels before 3.2.0-5
7377     level->use_time_orb_bug = TRUE;
7378
7379     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7380     level->block_snap_field = FALSE;
7381
7382     // extra time score was same value as time left score before 3.2.0-5
7383     level->extra_time_score = level->score[SC_TIME_BONUS];
7384   }
7385
7386   if (level->game_version < VERSION_IDENT(3,2,0,7))
7387   {
7388     // default behaviour for snapping was "not continuous" before 3.2.0-7
7389     level->continuous_snapping = FALSE;
7390   }
7391
7392   // only few elements were able to actively move into acid before 3.1.0
7393   // trigger settings did not exist before 3.1.0; set to default "any"
7394   if (level->game_version < VERSION_IDENT(3,1,0,0))
7395   {
7396     // correct "can move into acid" settings (all zero in old levels)
7397
7398     level->can_move_into_acid_bits = 0; // nothing can move into acid
7399     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7400
7401     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7402     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7403     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7404     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7405
7406     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7407       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7408
7409     // correct trigger settings (stored as zero == "none" in old levels)
7410
7411     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7412     {
7413       int element = EL_CUSTOM_START + i;
7414       struct ElementInfo *ei = &element_info[element];
7415
7416       for (j = 0; j < ei->num_change_pages; j++)
7417       {
7418         struct ElementChangeInfo *change = &ei->change_page[j];
7419
7420         change->trigger_player = CH_PLAYER_ANY;
7421         change->trigger_page = CH_PAGE_ANY;
7422       }
7423     }
7424   }
7425
7426   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7427   {
7428     int element = EL_CUSTOM_256;
7429     struct ElementInfo *ei = &element_info[element];
7430     struct ElementChangeInfo *change = &ei->change_page[0];
7431
7432     /* This is needed to fix a problem that was caused by a bugfix in function
7433        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7434        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7435        not replace walkable elements, but instead just placed the player on it,
7436        without placing the Sokoban field under the player). Unfortunately, this
7437        breaks "Snake Bite" style levels when the snake is halfway through a door
7438        that just closes (the snake head is still alive and can be moved in this
7439        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7440        player (without Sokoban element) which then gets killed as designed). */
7441
7442     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7443          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7444         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7445       change->target_element = EL_PLAYER_1;
7446   }
7447
7448   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7449   if (level->game_version < VERSION_IDENT(3,2,5,0))
7450   {
7451     /* This is needed to fix a problem that was caused by a bugfix in function
7452        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7453        corrects the behaviour when a custom element changes to another custom
7454        element with a higher element number that has change actions defined.
7455        Normally, only one change per frame is allowed for custom elements.
7456        Therefore, it is checked if a custom element already changed in the
7457        current frame; if it did, subsequent changes are suppressed.
7458        Unfortunately, this is only checked for element changes, but not for
7459        change actions, which are still executed. As the function above loops
7460        through all custom elements from lower to higher, an element change
7461        resulting in a lower CE number won't be checked again, while a target
7462        element with a higher number will also be checked, and potential change
7463        actions will get executed for this CE, too (which is wrong), while
7464        further changes are ignored (which is correct). As this bugfix breaks
7465        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7466        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7467        behaviour for existing levels and tapes that make use of this bug */
7468
7469     level->use_action_after_change_bug = TRUE;
7470   }
7471
7472   // not centering level after relocating player was default only in 3.2.3
7473   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7474     level->shifted_relocation = TRUE;
7475
7476   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7477   if (level->game_version < VERSION_IDENT(3,2,6,0))
7478     level->em_explodes_by_fire = TRUE;
7479
7480   // levels were solved by the first player entering an exit up to 4.1.0.0
7481   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7482     level->solved_by_one_player = TRUE;
7483
7484   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7485   if (level->game_version < VERSION_IDENT(4,1,1,1))
7486     level->use_life_bugs = TRUE;
7487
7488   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7489   if (level->game_version < VERSION_IDENT(4,1,1,1))
7490     level->sb_objects_needed = FALSE;
7491
7492   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7493   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7494     level->finish_dig_collect = FALSE;
7495
7496   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7497   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7498     level->keep_walkable_ce = TRUE;
7499 }
7500
7501 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7502 {
7503   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7504   int x, y;
7505
7506   // check if this level is (not) a Sokoban level
7507   for (y = 0; y < level->fieldy; y++)
7508     for (x = 0; x < level->fieldx; x++)
7509       if (!IS_SB_ELEMENT(Tile[x][y]))
7510         is_sokoban_level = FALSE;
7511
7512   if (is_sokoban_level)
7513   {
7514     // set special level settings for Sokoban levels
7515     SetLevelSettings_SB(level);
7516   }
7517 }
7518
7519 static void LoadLevel_InitSettings(struct LevelInfo *level)
7520 {
7521   // adjust level settings for (non-native) Sokoban-style levels
7522   LoadLevel_InitSettings_SB(level);
7523
7524   // rename levels with title "nameless level" or if renaming is forced
7525   if (leveldir_current->empty_level_name != NULL &&
7526       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7527        leveldir_current->force_level_name))
7528     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7529              leveldir_current->empty_level_name, level_nr);
7530 }
7531
7532 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7533 {
7534   int i, x, y;
7535
7536   // map elements that have changed in newer versions
7537   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7538                                                     level->game_version);
7539   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7540     for (x = 0; x < 3; x++)
7541       for (y = 0; y < 3; y++)
7542         level->yamyam_content[i].e[x][y] =
7543           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7544                                     level->game_version);
7545
7546 }
7547
7548 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7549 {
7550   int i, j;
7551
7552   // map custom element change events that have changed in newer versions
7553   // (these following values were accidentally changed in version 3.0.1)
7554   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7555   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7556   {
7557     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7558     {
7559       int element = EL_CUSTOM_START + i;
7560
7561       // order of checking and copying events to be mapped is important
7562       // (do not change the start and end value -- they are constant)
7563       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7564       {
7565         if (HAS_CHANGE_EVENT(element, j - 2))
7566         {
7567           SET_CHANGE_EVENT(element, j - 2, FALSE);
7568           SET_CHANGE_EVENT(element, j, TRUE);
7569         }
7570       }
7571
7572       // order of checking and copying events to be mapped is important
7573       // (do not change the start and end value -- they are constant)
7574       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7575       {
7576         if (HAS_CHANGE_EVENT(element, j - 1))
7577         {
7578           SET_CHANGE_EVENT(element, j - 1, FALSE);
7579           SET_CHANGE_EVENT(element, j, TRUE);
7580         }
7581       }
7582     }
7583   }
7584
7585   // initialize "can_change" field for old levels with only one change page
7586   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7587   {
7588     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7589     {
7590       int element = EL_CUSTOM_START + i;
7591
7592       if (CAN_CHANGE(element))
7593         element_info[element].change->can_change = TRUE;
7594     }
7595   }
7596
7597   // correct custom element values (for old levels without these options)
7598   if (level->game_version < VERSION_IDENT(3,1,1,0))
7599   {
7600     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7601     {
7602       int element = EL_CUSTOM_START + i;
7603       struct ElementInfo *ei = &element_info[element];
7604
7605       if (ei->access_direction == MV_NO_DIRECTION)
7606         ei->access_direction = MV_ALL_DIRECTIONS;
7607     }
7608   }
7609
7610   // correct custom element values (fix invalid values for all versions)
7611   if (1)
7612   {
7613     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7614     {
7615       int element = EL_CUSTOM_START + i;
7616       struct ElementInfo *ei = &element_info[element];
7617
7618       for (j = 0; j < ei->num_change_pages; j++)
7619       {
7620         struct ElementChangeInfo *change = &ei->change_page[j];
7621
7622         if (change->trigger_player == CH_PLAYER_NONE)
7623           change->trigger_player = CH_PLAYER_ANY;
7624
7625         if (change->trigger_side == CH_SIDE_NONE)
7626           change->trigger_side = CH_SIDE_ANY;
7627       }
7628     }
7629   }
7630
7631   // initialize "can_explode" field for old levels which did not store this
7632   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7633   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7634   {
7635     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7636     {
7637       int element = EL_CUSTOM_START + i;
7638
7639       if (EXPLODES_1X1_OLD(element))
7640         element_info[element].explosion_type = EXPLODES_1X1;
7641
7642       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7643                                              EXPLODES_SMASHED(element) ||
7644                                              EXPLODES_IMPACT(element)));
7645     }
7646   }
7647
7648   // correct previously hard-coded move delay values for maze runner style
7649   if (level->game_version < VERSION_IDENT(3,1,1,0))
7650   {
7651     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7652     {
7653       int element = EL_CUSTOM_START + i;
7654
7655       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7656       {
7657         // previously hard-coded and therefore ignored
7658         element_info[element].move_delay_fixed = 9;
7659         element_info[element].move_delay_random = 0;
7660       }
7661     }
7662   }
7663
7664   // set some other uninitialized values of custom elements in older levels
7665   if (level->game_version < VERSION_IDENT(3,1,0,0))
7666   {
7667     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7668     {
7669       int element = EL_CUSTOM_START + i;
7670
7671       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7672
7673       element_info[element].explosion_delay = 17;
7674       element_info[element].ignition_delay = 8;
7675     }
7676   }
7677
7678   // set mouse click change events to work for left/middle/right mouse button
7679   if (level->game_version < VERSION_IDENT(4,2,3,0))
7680   {
7681     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7682     {
7683       int element = EL_CUSTOM_START + i;
7684       struct ElementInfo *ei = &element_info[element];
7685
7686       for (j = 0; j < ei->num_change_pages; j++)
7687       {
7688         struct ElementChangeInfo *change = &ei->change_page[j];
7689
7690         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7691             change->has_event[CE_PRESSED_BY_MOUSE] ||
7692             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7693             change->has_event[CE_MOUSE_PRESSED_ON_X])
7694           change->trigger_side = CH_SIDE_ANY;
7695       }
7696     }
7697   }
7698 }
7699
7700 static void LoadLevel_InitElements(struct LevelInfo *level)
7701 {
7702   LoadLevel_InitStandardElements(level);
7703
7704   if (level->file_has_custom_elements)
7705     LoadLevel_InitCustomElements(level);
7706
7707   // initialize element properties for level editor etc.
7708   InitElementPropertiesEngine(level->game_version);
7709   InitElementPropertiesGfxElement();
7710 }
7711
7712 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7713 {
7714   int x, y;
7715
7716   // map elements that have changed in newer versions
7717   for (y = 0; y < level->fieldy; y++)
7718     for (x = 0; x < level->fieldx; x++)
7719       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7720                                                      level->game_version);
7721
7722   // clear unused playfield data (nicer if level gets resized in editor)
7723   for (x = 0; x < MAX_LEV_FIELDX; x++)
7724     for (y = 0; y < MAX_LEV_FIELDY; y++)
7725       if (x >= level->fieldx || y >= level->fieldy)
7726         level->field[x][y] = EL_EMPTY;
7727
7728   // copy elements to runtime playfield array
7729   for (x = 0; x < MAX_LEV_FIELDX; x++)
7730     for (y = 0; y < MAX_LEV_FIELDY; y++)
7731       Tile[x][y] = level->field[x][y];
7732
7733   // initialize level size variables for faster access
7734   lev_fieldx = level->fieldx;
7735   lev_fieldy = level->fieldy;
7736
7737   // determine border element for this level
7738   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7739     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7740   else
7741     SetBorderElement();
7742 }
7743
7744 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7745 {
7746   struct LevelFileInfo *level_file_info = &level->file_info;
7747
7748   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7749     CopyNativeLevel_RND_to_Native(level);
7750 }
7751
7752 static void LoadLevelTemplate_LoadAndInit(void)
7753 {
7754   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7755
7756   LoadLevel_InitVersion(&level_template);
7757   LoadLevel_InitElements(&level_template);
7758   LoadLevel_InitSettings(&level_template);
7759
7760   ActivateLevelTemplate();
7761 }
7762
7763 void LoadLevelTemplate(int nr)
7764 {
7765   if (!fileExists(getGlobalLevelTemplateFilename()))
7766   {
7767     Warn("no level template found for this level");
7768
7769     return;
7770   }
7771
7772   setLevelFileInfo(&level_template.file_info, nr);
7773
7774   LoadLevelTemplate_LoadAndInit();
7775 }
7776
7777 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7778 {
7779   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7780
7781   LoadLevelTemplate_LoadAndInit();
7782 }
7783
7784 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7785 {
7786   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7787
7788   if (level.use_custom_template)
7789   {
7790     if (network_level != NULL)
7791       LoadNetworkLevelTemplate(network_level);
7792     else
7793       LoadLevelTemplate(-1);
7794   }
7795
7796   LoadLevel_InitVersion(&level);
7797   LoadLevel_InitElements(&level);
7798   LoadLevel_InitPlayfield(&level);
7799   LoadLevel_InitSettings(&level);
7800
7801   LoadLevel_InitNativeEngines(&level);
7802 }
7803
7804 void LoadLevel(int nr)
7805 {
7806   SetLevelSetInfo(leveldir_current->identifier, nr);
7807
7808   setLevelFileInfo(&level.file_info, nr);
7809
7810   LoadLevel_LoadAndInit(NULL);
7811 }
7812
7813 void LoadLevelInfoOnly(int nr)
7814 {
7815   setLevelFileInfo(&level.file_info, nr);
7816
7817   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7818 }
7819
7820 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7821 {
7822   SetLevelSetInfo(network_level->leveldir_identifier,
7823                   network_level->file_info.nr);
7824
7825   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7826
7827   LoadLevel_LoadAndInit(network_level);
7828 }
7829
7830 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7831 {
7832   int chunk_size = 0;
7833
7834   chunk_size += putFileVersion(file, level->file_version);
7835   chunk_size += putFileVersion(file, level->game_version);
7836
7837   return chunk_size;
7838 }
7839
7840 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7841 {
7842   int chunk_size = 0;
7843
7844   chunk_size += putFile16BitBE(file, level->creation_date.year);
7845   chunk_size += putFile8Bit(file,    level->creation_date.month);
7846   chunk_size += putFile8Bit(file,    level->creation_date.day);
7847
7848   return chunk_size;
7849 }
7850
7851 #if ENABLE_HISTORIC_CHUNKS
7852 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7853 {
7854   int i, x, y;
7855
7856   putFile8Bit(file, level->fieldx);
7857   putFile8Bit(file, level->fieldy);
7858
7859   putFile16BitBE(file, level->time);
7860   putFile16BitBE(file, level->gems_needed);
7861
7862   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7863     putFile8Bit(file, level->name[i]);
7864
7865   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7866     putFile8Bit(file, level->score[i]);
7867
7868   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7869     for (y = 0; y < 3; y++)
7870       for (x = 0; x < 3; x++)
7871         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7872                            level->yamyam_content[i].e[x][y]));
7873   putFile8Bit(file, level->amoeba_speed);
7874   putFile8Bit(file, level->time_magic_wall);
7875   putFile8Bit(file, level->time_wheel);
7876   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7877                      level->amoeba_content));
7878   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7879   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7880   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7881   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7882
7883   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7884
7885   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7886   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7887   putFile32BitBE(file, level->can_move_into_acid_bits);
7888   putFile8Bit(file, level->dont_collide_with_bits);
7889
7890   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7891   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7892
7893   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7894   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7895   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7896
7897   putFile8Bit(file, level->game_engine_type);
7898
7899   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7900 }
7901 #endif
7902
7903 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7904 {
7905   int chunk_size = 0;
7906   int i;
7907
7908   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7909     chunk_size += putFile8Bit(file, level->name[i]);
7910
7911   return chunk_size;
7912 }
7913
7914 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7915 {
7916   int chunk_size = 0;
7917   int i;
7918
7919   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7920     chunk_size += putFile8Bit(file, level->author[i]);
7921
7922   return chunk_size;
7923 }
7924
7925 #if ENABLE_HISTORIC_CHUNKS
7926 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7927 {
7928   int chunk_size = 0;
7929   int x, y;
7930
7931   for (y = 0; y < level->fieldy; y++)
7932     for (x = 0; x < level->fieldx; x++)
7933       if (level->encoding_16bit_field)
7934         chunk_size += putFile16BitBE(file, level->field[x][y]);
7935       else
7936         chunk_size += putFile8Bit(file, level->field[x][y]);
7937
7938   return chunk_size;
7939 }
7940 #endif
7941
7942 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7943 {
7944   int chunk_size = 0;
7945   int x, y;
7946
7947   for (y = 0; y < level->fieldy; y++) 
7948     for (x = 0; x < level->fieldx; x++) 
7949       chunk_size += putFile16BitBE(file, level->field[x][y]);
7950
7951   return chunk_size;
7952 }
7953
7954 #if ENABLE_HISTORIC_CHUNKS
7955 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7956 {
7957   int i, x, y;
7958
7959   putFile8Bit(file, EL_YAMYAM);
7960   putFile8Bit(file, level->num_yamyam_contents);
7961   putFile8Bit(file, 0);
7962   putFile8Bit(file, 0);
7963
7964   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7965     for (y = 0; y < 3; y++)
7966       for (x = 0; x < 3; x++)
7967         if (level->encoding_16bit_field)
7968           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7969         else
7970           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7971 }
7972 #endif
7973
7974 #if ENABLE_HISTORIC_CHUNKS
7975 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7976 {
7977   int i, x, y;
7978   int num_contents, content_xsize, content_ysize;
7979   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7980
7981   if (element == EL_YAMYAM)
7982   {
7983     num_contents = level->num_yamyam_contents;
7984     content_xsize = 3;
7985     content_ysize = 3;
7986
7987     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7988       for (y = 0; y < 3; y++)
7989         for (x = 0; x < 3; x++)
7990           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7991   }
7992   else if (element == EL_BD_AMOEBA)
7993   {
7994     num_contents = 1;
7995     content_xsize = 1;
7996     content_ysize = 1;
7997
7998     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7999       for (y = 0; y < 3; y++)
8000         for (x = 0; x < 3; x++)
8001           content_array[i][x][y] = EL_EMPTY;
8002     content_array[0][0][0] = level->amoeba_content;
8003   }
8004   else
8005   {
8006     // chunk header already written -- write empty chunk data
8007     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8008
8009     Warn("cannot save content for element '%d'", element);
8010
8011     return;
8012   }
8013
8014   putFile16BitBE(file, element);
8015   putFile8Bit(file, num_contents);
8016   putFile8Bit(file, content_xsize);
8017   putFile8Bit(file, content_ysize);
8018
8019   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8020
8021   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8022     for (y = 0; y < 3; y++)
8023       for (x = 0; x < 3; x++)
8024         putFile16BitBE(file, content_array[i][x][y]);
8025 }
8026 #endif
8027
8028 #if ENABLE_HISTORIC_CHUNKS
8029 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8030 {
8031   int envelope_nr = element - EL_ENVELOPE_1;
8032   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8033   int chunk_size = 0;
8034   int i;
8035
8036   chunk_size += putFile16BitBE(file, element);
8037   chunk_size += putFile16BitBE(file, envelope_len);
8038   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8039   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8040
8041   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8042   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8043
8044   for (i = 0; i < envelope_len; i++)
8045     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8046
8047   return chunk_size;
8048 }
8049 #endif
8050
8051 #if ENABLE_HISTORIC_CHUNKS
8052 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8053                            int num_changed_custom_elements)
8054 {
8055   int i, check = 0;
8056
8057   putFile16BitBE(file, num_changed_custom_elements);
8058
8059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8060   {
8061     int element = EL_CUSTOM_START + i;
8062
8063     struct ElementInfo *ei = &element_info[element];
8064
8065     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8066     {
8067       if (check < num_changed_custom_elements)
8068       {
8069         putFile16BitBE(file, element);
8070         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8071       }
8072
8073       check++;
8074     }
8075   }
8076
8077   if (check != num_changed_custom_elements)     // should not happen
8078     Warn("inconsistent number of custom element properties");
8079 }
8080 #endif
8081
8082 #if ENABLE_HISTORIC_CHUNKS
8083 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8084                            int num_changed_custom_elements)
8085 {
8086   int i, check = 0;
8087
8088   putFile16BitBE(file, num_changed_custom_elements);
8089
8090   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8091   {
8092     int element = EL_CUSTOM_START + i;
8093
8094     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8095     {
8096       if (check < num_changed_custom_elements)
8097       {
8098         putFile16BitBE(file, element);
8099         putFile16BitBE(file, element_info[element].change->target_element);
8100       }
8101
8102       check++;
8103     }
8104   }
8105
8106   if (check != num_changed_custom_elements)     // should not happen
8107     Warn("inconsistent number of custom target elements");
8108 }
8109 #endif
8110
8111 #if ENABLE_HISTORIC_CHUNKS
8112 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8113                            int num_changed_custom_elements)
8114 {
8115   int i, j, x, y, check = 0;
8116
8117   putFile16BitBE(file, num_changed_custom_elements);
8118
8119   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8120   {
8121     int element = EL_CUSTOM_START + i;
8122     struct ElementInfo *ei = &element_info[element];
8123
8124     if (ei->modified_settings)
8125     {
8126       if (check < num_changed_custom_elements)
8127       {
8128         putFile16BitBE(file, element);
8129
8130         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8131           putFile8Bit(file, ei->description[j]);
8132
8133         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8134
8135         // some free bytes for future properties and padding
8136         WriteUnusedBytesToFile(file, 7);
8137
8138         putFile8Bit(file, ei->use_gfx_element);
8139         putFile16BitBE(file, ei->gfx_element_initial);
8140
8141         putFile8Bit(file, ei->collect_score_initial);
8142         putFile8Bit(file, ei->collect_count_initial);
8143
8144         putFile16BitBE(file, ei->push_delay_fixed);
8145         putFile16BitBE(file, ei->push_delay_random);
8146         putFile16BitBE(file, ei->move_delay_fixed);
8147         putFile16BitBE(file, ei->move_delay_random);
8148
8149         putFile16BitBE(file, ei->move_pattern);
8150         putFile8Bit(file, ei->move_direction_initial);
8151         putFile8Bit(file, ei->move_stepsize);
8152
8153         for (y = 0; y < 3; y++)
8154           for (x = 0; x < 3; x++)
8155             putFile16BitBE(file, ei->content.e[x][y]);
8156
8157         putFile32BitBE(file, ei->change->events);
8158
8159         putFile16BitBE(file, ei->change->target_element);
8160
8161         putFile16BitBE(file, ei->change->delay_fixed);
8162         putFile16BitBE(file, ei->change->delay_random);
8163         putFile16BitBE(file, ei->change->delay_frames);
8164
8165         putFile16BitBE(file, ei->change->initial_trigger_element);
8166
8167         putFile8Bit(file, ei->change->explode);
8168         putFile8Bit(file, ei->change->use_target_content);
8169         putFile8Bit(file, ei->change->only_if_complete);
8170         putFile8Bit(file, ei->change->use_random_replace);
8171
8172         putFile8Bit(file, ei->change->random_percentage);
8173         putFile8Bit(file, ei->change->replace_when);
8174
8175         for (y = 0; y < 3; y++)
8176           for (x = 0; x < 3; x++)
8177             putFile16BitBE(file, ei->change->content.e[x][y]);
8178
8179         putFile8Bit(file, ei->slippery_type);
8180
8181         // some free bytes for future properties and padding
8182         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8183       }
8184
8185       check++;
8186     }
8187   }
8188
8189   if (check != num_changed_custom_elements)     // should not happen
8190     Warn("inconsistent number of custom element properties");
8191 }
8192 #endif
8193
8194 #if ENABLE_HISTORIC_CHUNKS
8195 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8196 {
8197   struct ElementInfo *ei = &element_info[element];
8198   int i, j, x, y;
8199
8200   // ---------- custom element base property values (96 bytes) ----------------
8201
8202   putFile16BitBE(file, element);
8203
8204   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8205     putFile8Bit(file, ei->description[i]);
8206
8207   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8208
8209   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8210
8211   putFile8Bit(file, ei->num_change_pages);
8212
8213   putFile16BitBE(file, ei->ce_value_fixed_initial);
8214   putFile16BitBE(file, ei->ce_value_random_initial);
8215   putFile8Bit(file, ei->use_last_ce_value);
8216
8217   putFile8Bit(file, ei->use_gfx_element);
8218   putFile16BitBE(file, ei->gfx_element_initial);
8219
8220   putFile8Bit(file, ei->collect_score_initial);
8221   putFile8Bit(file, ei->collect_count_initial);
8222
8223   putFile8Bit(file, ei->drop_delay_fixed);
8224   putFile8Bit(file, ei->push_delay_fixed);
8225   putFile8Bit(file, ei->drop_delay_random);
8226   putFile8Bit(file, ei->push_delay_random);
8227   putFile16BitBE(file, ei->move_delay_fixed);
8228   putFile16BitBE(file, ei->move_delay_random);
8229
8230   // bits 0 - 15 of "move_pattern" ...
8231   putFile16BitBE(file, ei->move_pattern & 0xffff);
8232   putFile8Bit(file, ei->move_direction_initial);
8233   putFile8Bit(file, ei->move_stepsize);
8234
8235   putFile8Bit(file, ei->slippery_type);
8236
8237   for (y = 0; y < 3; y++)
8238     for (x = 0; x < 3; x++)
8239       putFile16BitBE(file, ei->content.e[x][y]);
8240
8241   putFile16BitBE(file, ei->move_enter_element);
8242   putFile16BitBE(file, ei->move_leave_element);
8243   putFile8Bit(file, ei->move_leave_type);
8244
8245   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8246   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8247
8248   putFile8Bit(file, ei->access_direction);
8249
8250   putFile8Bit(file, ei->explosion_delay);
8251   putFile8Bit(file, ei->ignition_delay);
8252   putFile8Bit(file, ei->explosion_type);
8253
8254   // some free bytes for future custom property values and padding
8255   WriteUnusedBytesToFile(file, 1);
8256
8257   // ---------- change page property values (48 bytes) ------------------------
8258
8259   for (i = 0; i < ei->num_change_pages; i++)
8260   {
8261     struct ElementChangeInfo *change = &ei->change_page[i];
8262     unsigned int event_bits;
8263
8264     // bits 0 - 31 of "has_event[]" ...
8265     event_bits = 0;
8266     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8267       if (change->has_event[j])
8268         event_bits |= (1u << j);
8269     putFile32BitBE(file, event_bits);
8270
8271     putFile16BitBE(file, change->target_element);
8272
8273     putFile16BitBE(file, change->delay_fixed);
8274     putFile16BitBE(file, change->delay_random);
8275     putFile16BitBE(file, change->delay_frames);
8276
8277     putFile16BitBE(file, change->initial_trigger_element);
8278
8279     putFile8Bit(file, change->explode);
8280     putFile8Bit(file, change->use_target_content);
8281     putFile8Bit(file, change->only_if_complete);
8282     putFile8Bit(file, change->use_random_replace);
8283
8284     putFile8Bit(file, change->random_percentage);
8285     putFile8Bit(file, change->replace_when);
8286
8287     for (y = 0; y < 3; y++)
8288       for (x = 0; x < 3; x++)
8289         putFile16BitBE(file, change->target_content.e[x][y]);
8290
8291     putFile8Bit(file, change->can_change);
8292
8293     putFile8Bit(file, change->trigger_side);
8294
8295     putFile8Bit(file, change->trigger_player);
8296     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8297                        log_2(change->trigger_page)));
8298
8299     putFile8Bit(file, change->has_action);
8300     putFile8Bit(file, change->action_type);
8301     putFile8Bit(file, change->action_mode);
8302     putFile16BitBE(file, change->action_arg);
8303
8304     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8305     event_bits = 0;
8306     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8307       if (change->has_event[j])
8308         event_bits |= (1u << (j - 32));
8309     putFile8Bit(file, event_bits);
8310   }
8311 }
8312 #endif
8313
8314 #if ENABLE_HISTORIC_CHUNKS
8315 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8316 {
8317   struct ElementInfo *ei = &element_info[element];
8318   struct ElementGroupInfo *group = ei->group;
8319   int i;
8320
8321   putFile16BitBE(file, element);
8322
8323   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8324     putFile8Bit(file, ei->description[i]);
8325
8326   putFile8Bit(file, group->num_elements);
8327
8328   putFile8Bit(file, ei->use_gfx_element);
8329   putFile16BitBE(file, ei->gfx_element_initial);
8330
8331   putFile8Bit(file, group->choice_mode);
8332
8333   // some free bytes for future values and padding
8334   WriteUnusedBytesToFile(file, 3);
8335
8336   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8337     putFile16BitBE(file, group->element[i]);
8338 }
8339 #endif
8340
8341 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8342                                 boolean write_element)
8343 {
8344   int save_type = entry->save_type;
8345   int data_type = entry->data_type;
8346   int conf_type = entry->conf_type;
8347   int byte_mask = conf_type & CONF_MASK_BYTES;
8348   int element = entry->element;
8349   int default_value = entry->default_value;
8350   int num_bytes = 0;
8351   boolean modified = FALSE;
8352
8353   if (byte_mask != CONF_MASK_MULTI_BYTES)
8354   {
8355     void *value_ptr = entry->value;
8356     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8357                  *(int *)value_ptr);
8358
8359     // check if any settings have been modified before saving them
8360     if (value != default_value)
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 += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8373                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8374                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8375                   0);
8376   }
8377   else if (data_type == TYPE_STRING)
8378   {
8379     char *default_string = entry->default_string;
8380     char *string = (char *)(entry->value);
8381     int string_length = strlen(string);
8382     int i;
8383
8384     // check if any settings have been modified before saving them
8385     if (!strEqual(string, default_string))
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, string_length);
8398
8399     for (i = 0; i < string_length; i++)
8400       num_bytes += putFile8Bit(file, string[i]);
8401   }
8402   else if (data_type == TYPE_ELEMENT_LIST)
8403   {
8404     int *element_array = (int *)(entry->value);
8405     int num_elements = *(int *)(entry->num_entities);
8406     int i;
8407
8408     // check if any settings have been modified before saving them
8409     for (i = 0; i < num_elements; i++)
8410       if (element_array[i] != default_value)
8411         modified = TRUE;
8412
8413     // do not save if explicitly told or if unmodified default settings
8414     if ((save_type == SAVE_CONF_NEVER) ||
8415         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8416       return 0;
8417
8418     if (write_element)
8419       num_bytes += putFile16BitBE(file, element);
8420
8421     num_bytes += putFile8Bit(file, conf_type);
8422     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8423
8424     for (i = 0; i < num_elements; i++)
8425       num_bytes += putFile16BitBE(file, element_array[i]);
8426   }
8427   else if (data_type == TYPE_CONTENT_LIST)
8428   {
8429     struct Content *content = (struct Content *)(entry->value);
8430     int num_contents = *(int *)(entry->num_entities);
8431     int i, x, y;
8432
8433     // check if any settings have been modified before saving them
8434     for (i = 0; i < num_contents; i++)
8435       for (y = 0; y < 3; y++)
8436         for (x = 0; x < 3; x++)
8437           if (content[i].e[x][y] != default_value)
8438             modified = TRUE;
8439
8440     // do not save if explicitly told or if unmodified default settings
8441     if ((save_type == SAVE_CONF_NEVER) ||
8442         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8443       return 0;
8444
8445     if (write_element)
8446       num_bytes += putFile16BitBE(file, element);
8447
8448     num_bytes += putFile8Bit(file, conf_type);
8449     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8450
8451     for (i = 0; i < num_contents; i++)
8452       for (y = 0; y < 3; y++)
8453         for (x = 0; x < 3; x++)
8454           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8455   }
8456
8457   return num_bytes;
8458 }
8459
8460 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8461 {
8462   int chunk_size = 0;
8463   int i;
8464
8465   li = *level;          // copy level data into temporary buffer
8466
8467   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8468     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8469
8470   return chunk_size;
8471 }
8472
8473 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8474 {
8475   int chunk_size = 0;
8476   int i;
8477
8478   li = *level;          // copy level data into temporary buffer
8479
8480   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8481     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8482
8483   return chunk_size;
8484 }
8485
8486 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8487 {
8488   int envelope_nr = element - EL_ENVELOPE_1;
8489   int chunk_size = 0;
8490   int i;
8491
8492   chunk_size += putFile16BitBE(file, element);
8493
8494   // copy envelope data into temporary buffer
8495   xx_envelope = level->envelope[envelope_nr];
8496
8497   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8498     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8499
8500   return chunk_size;
8501 }
8502
8503 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8504 {
8505   struct ElementInfo *ei = &element_info[element];
8506   int chunk_size = 0;
8507   int i, j;
8508
8509   chunk_size += putFile16BitBE(file, element);
8510
8511   xx_ei = *ei;          // copy element data into temporary buffer
8512
8513   // set default description string for this specific element
8514   strcpy(xx_default_description, getDefaultElementDescription(ei));
8515
8516   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8517     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8518
8519   for (i = 0; i < ei->num_change_pages; i++)
8520   {
8521     struct ElementChangeInfo *change = &ei->change_page[i];
8522
8523     xx_current_change_page = i;
8524
8525     xx_change = *change;        // copy change data into temporary buffer
8526
8527     resetEventBits();
8528     setEventBitsFromEventFlags(change);
8529
8530     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8531       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8532                                          FALSE);
8533   }
8534
8535   return chunk_size;
8536 }
8537
8538 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8539 {
8540   struct ElementInfo *ei = &element_info[element];
8541   struct ElementGroupInfo *group = ei->group;
8542   int chunk_size = 0;
8543   int i;
8544
8545   chunk_size += putFile16BitBE(file, element);
8546
8547   xx_ei = *ei;          // copy element data into temporary buffer
8548   xx_group = *group;    // copy group data into temporary buffer
8549
8550   // set default description string for this specific element
8551   strcpy(xx_default_description, getDefaultElementDescription(ei));
8552
8553   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8554     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8555
8556   return chunk_size;
8557 }
8558
8559 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8560 {
8561   struct ElementInfo *ei = &element_info[element];
8562   int chunk_size = 0;
8563   int i;
8564
8565   chunk_size += putFile16BitBE(file, element);
8566
8567   xx_ei = *ei;          // copy element data into temporary buffer
8568
8569   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8570     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8571
8572   return chunk_size;
8573 }
8574
8575 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8576                                   boolean save_as_template)
8577 {
8578   int chunk_size;
8579   int i;
8580   FILE *file;
8581
8582   if (!(file = fopen(filename, MODE_WRITE)))
8583   {
8584     Warn("cannot save level file '%s'", filename);
8585
8586     return;
8587   }
8588
8589   level->file_version = FILE_VERSION_ACTUAL;
8590   level->game_version = GAME_VERSION_ACTUAL;
8591
8592   level->creation_date = getCurrentDate();
8593
8594   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8595   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8596
8597   chunk_size = SaveLevel_VERS(NULL, level);
8598   putFileChunkBE(file, "VERS", chunk_size);
8599   SaveLevel_VERS(file, level);
8600
8601   chunk_size = SaveLevel_DATE(NULL, level);
8602   putFileChunkBE(file, "DATE", chunk_size);
8603   SaveLevel_DATE(file, level);
8604
8605   chunk_size = SaveLevel_NAME(NULL, level);
8606   putFileChunkBE(file, "NAME", chunk_size);
8607   SaveLevel_NAME(file, level);
8608
8609   chunk_size = SaveLevel_AUTH(NULL, level);
8610   putFileChunkBE(file, "AUTH", chunk_size);
8611   SaveLevel_AUTH(file, level);
8612
8613   chunk_size = SaveLevel_INFO(NULL, level);
8614   putFileChunkBE(file, "INFO", chunk_size);
8615   SaveLevel_INFO(file, level);
8616
8617   chunk_size = SaveLevel_BODY(NULL, level);
8618   putFileChunkBE(file, "BODY", chunk_size);
8619   SaveLevel_BODY(file, level);
8620
8621   chunk_size = SaveLevel_ELEM(NULL, level);
8622   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8623   {
8624     putFileChunkBE(file, "ELEM", chunk_size);
8625     SaveLevel_ELEM(file, level);
8626   }
8627
8628   for (i = 0; i < NUM_ENVELOPES; i++)
8629   {
8630     int element = EL_ENVELOPE_1 + i;
8631
8632     chunk_size = SaveLevel_NOTE(NULL, level, element);
8633     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8634     {
8635       putFileChunkBE(file, "NOTE", chunk_size);
8636       SaveLevel_NOTE(file, level, element);
8637     }
8638   }
8639
8640   // if not using template level, check for non-default custom/group elements
8641   if (!level->use_custom_template || save_as_template)
8642   {
8643     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8644     {
8645       int element = EL_CUSTOM_START + i;
8646
8647       chunk_size = SaveLevel_CUSX(NULL, level, element);
8648       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8649       {
8650         putFileChunkBE(file, "CUSX", chunk_size);
8651         SaveLevel_CUSX(file, level, element);
8652       }
8653     }
8654
8655     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8656     {
8657       int element = EL_GROUP_START + i;
8658
8659       chunk_size = SaveLevel_GRPX(NULL, level, element);
8660       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8661       {
8662         putFileChunkBE(file, "GRPX", chunk_size);
8663         SaveLevel_GRPX(file, level, element);
8664       }
8665     }
8666
8667     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8668     {
8669       int element = GET_EMPTY_ELEMENT(i);
8670
8671       chunk_size = SaveLevel_EMPX(NULL, level, element);
8672       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8673       {
8674         putFileChunkBE(file, "EMPX", chunk_size);
8675         SaveLevel_EMPX(file, level, element);
8676       }
8677     }
8678   }
8679
8680   fclose(file);
8681
8682   SetFilePermissions(filename, PERMS_PRIVATE);
8683 }
8684
8685 void SaveLevel(int nr)
8686 {
8687   char *filename = getDefaultLevelFilename(nr);
8688
8689   SaveLevelFromFilename(&level, filename, FALSE);
8690 }
8691
8692 void SaveLevelTemplate(void)
8693 {
8694   char *filename = getLocalLevelTemplateFilename();
8695
8696   SaveLevelFromFilename(&level, filename, TRUE);
8697 }
8698
8699 boolean SaveLevelChecked(int nr)
8700 {
8701   char *filename = getDefaultLevelFilename(nr);
8702   boolean new_level = !fileExists(filename);
8703   boolean level_saved = FALSE;
8704
8705   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8706   {
8707     SaveLevel(nr);
8708
8709     if (new_level)
8710       Request("Level saved!", REQ_CONFIRM);
8711
8712     level_saved = TRUE;
8713   }
8714
8715   return level_saved;
8716 }
8717
8718 void DumpLevel(struct LevelInfo *level)
8719 {
8720   if (level->no_level_file || level->no_valid_file)
8721   {
8722     Warn("cannot dump -- no valid level file found");
8723
8724     return;
8725   }
8726
8727   PrintLine("-", 79);
8728   Print("Level xxx (file version %08d, game version %08d)\n",
8729         level->file_version, level->game_version);
8730   PrintLine("-", 79);
8731
8732   Print("Level author: '%s'\n", level->author);
8733   Print("Level title:  '%s'\n", level->name);
8734   Print("\n");
8735   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8736   Print("\n");
8737   Print("Level time:  %d seconds\n", level->time);
8738   Print("Gems needed: %d\n", level->gems_needed);
8739   Print("\n");
8740   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8741   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8742   Print("Time for light:      %d seconds\n", level->time_light);
8743   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8744   Print("\n");
8745   Print("Amoeba speed: %d\n", level->amoeba_speed);
8746   Print("\n");
8747
8748   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8749   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8750   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8751   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8752   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8753   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8754
8755   if (options.debug)
8756   {
8757     int i, j;
8758
8759     for (i = 0; i < NUM_ENVELOPES; i++)
8760     {
8761       char *text = level->envelope[i].text;
8762       int text_len = strlen(text);
8763       boolean has_text = FALSE;
8764
8765       for (j = 0; j < text_len; j++)
8766         if (text[j] != ' ' && text[j] != '\n')
8767           has_text = TRUE;
8768
8769       if (has_text)
8770       {
8771         Print("\n");
8772         Print("Envelope %d:\n'%s'\n", i + 1, text);
8773       }
8774     }
8775   }
8776
8777   PrintLine("-", 79);
8778 }
8779
8780 void DumpLevels(void)
8781 {
8782   static LevelDirTree *dumplevel_leveldir = NULL;
8783
8784   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8785                                                  global.dumplevel_leveldir);
8786
8787   if (dumplevel_leveldir == NULL)
8788     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8789
8790   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8791       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8792     Fail("no such level number: %d", global.dumplevel_level_nr);
8793
8794   leveldir_current = dumplevel_leveldir;
8795
8796   LoadLevel(global.dumplevel_level_nr);
8797   DumpLevel(&level);
8798
8799   CloseAllAndExit(0);
8800 }
8801
8802
8803 // ============================================================================
8804 // tape file functions
8805 // ============================================================================
8806
8807 static void setTapeInfoToDefaults(void)
8808 {
8809   int i;
8810
8811   // always start with reliable default values (empty tape)
8812   TapeErase();
8813
8814   // default values (also for pre-1.2 tapes) with only the first player
8815   tape.player_participates[0] = TRUE;
8816   for (i = 1; i < MAX_PLAYERS; i++)
8817     tape.player_participates[i] = FALSE;
8818
8819   // at least one (default: the first) player participates in every tape
8820   tape.num_participating_players = 1;
8821
8822   tape.property_bits = TAPE_PROPERTY_NONE;
8823
8824   tape.level_nr = level_nr;
8825   tape.counter = 0;
8826   tape.changed = FALSE;
8827   tape.solved = FALSE;
8828
8829   tape.recording = FALSE;
8830   tape.playing = FALSE;
8831   tape.pausing = FALSE;
8832
8833   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8834   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8835
8836   tape.no_info_chunk = TRUE;
8837   tape.no_valid_file = FALSE;
8838 }
8839
8840 static int getTapePosSize(struct TapeInfo *tape)
8841 {
8842   int tape_pos_size = 0;
8843
8844   if (tape->use_key_actions)
8845     tape_pos_size += tape->num_participating_players;
8846
8847   if (tape->use_mouse_actions)
8848     tape_pos_size += 3;         // x and y position and mouse button mask
8849
8850   tape_pos_size += 1;           // tape action delay value
8851
8852   return tape_pos_size;
8853 }
8854
8855 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8856 {
8857   tape->use_key_actions = FALSE;
8858   tape->use_mouse_actions = FALSE;
8859
8860   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8861     tape->use_key_actions = TRUE;
8862
8863   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8864     tape->use_mouse_actions = TRUE;
8865 }
8866
8867 static int getTapeActionValue(struct TapeInfo *tape)
8868 {
8869   return (tape->use_key_actions &&
8870           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8871           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8872           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8873           TAPE_ACTIONS_DEFAULT);
8874 }
8875
8876 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8877 {
8878   tape->file_version = getFileVersion(file);
8879   tape->game_version = getFileVersion(file);
8880
8881   return chunk_size;
8882 }
8883
8884 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8885 {
8886   int i;
8887
8888   tape->random_seed = getFile32BitBE(file);
8889   tape->date        = getFile32BitBE(file);
8890   tape->length      = getFile32BitBE(file);
8891
8892   // read header fields that are new since version 1.2
8893   if (tape->file_version >= FILE_VERSION_1_2)
8894   {
8895     byte store_participating_players = getFile8Bit(file);
8896     int engine_version;
8897
8898     // since version 1.2, tapes store which players participate in the tape
8899     tape->num_participating_players = 0;
8900     for (i = 0; i < MAX_PLAYERS; i++)
8901     {
8902       tape->player_participates[i] = FALSE;
8903
8904       if (store_participating_players & (1 << i))
8905       {
8906         tape->player_participates[i] = TRUE;
8907         tape->num_participating_players++;
8908       }
8909     }
8910
8911     setTapeActionFlags(tape, getFile8Bit(file));
8912
8913     tape->property_bits = getFile8Bit(file);
8914     tape->solved = getFile8Bit(file);
8915
8916     engine_version = getFileVersion(file);
8917     if (engine_version > 0)
8918       tape->engine_version = engine_version;
8919     else
8920       tape->engine_version = tape->game_version;
8921   }
8922
8923   return chunk_size;
8924 }
8925
8926 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8927 {
8928   tape->scr_fieldx = getFile8Bit(file);
8929   tape->scr_fieldy = getFile8Bit(file);
8930
8931   return chunk_size;
8932 }
8933
8934 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8935 {
8936   char *level_identifier = NULL;
8937   int level_identifier_size;
8938   int i;
8939
8940   tape->no_info_chunk = FALSE;
8941
8942   level_identifier_size = getFile16BitBE(file);
8943
8944   level_identifier = checked_malloc(level_identifier_size);
8945
8946   for (i = 0; i < level_identifier_size; i++)
8947     level_identifier[i] = getFile8Bit(file);
8948
8949   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8950   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8951
8952   checked_free(level_identifier);
8953
8954   tape->level_nr = getFile16BitBE(file);
8955
8956   chunk_size = 2 + level_identifier_size + 2;
8957
8958   return chunk_size;
8959 }
8960
8961 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8962 {
8963   int i, j;
8964   int tape_pos_size = getTapePosSize(tape);
8965   int chunk_size_expected = tape_pos_size * tape->length;
8966
8967   if (chunk_size_expected != chunk_size)
8968   {
8969     ReadUnusedBytesFromFile(file, chunk_size);
8970     return chunk_size_expected;
8971   }
8972
8973   for (i = 0; i < tape->length; i++)
8974   {
8975     if (i >= MAX_TAPE_LEN)
8976     {
8977       Warn("tape truncated -- size exceeds maximum tape size %d",
8978             MAX_TAPE_LEN);
8979
8980       // tape too large; read and ignore remaining tape data from this chunk
8981       for (;i < tape->length; i++)
8982         ReadUnusedBytesFromFile(file, tape_pos_size);
8983
8984       break;
8985     }
8986
8987     if (tape->use_key_actions)
8988     {
8989       for (j = 0; j < MAX_PLAYERS; j++)
8990       {
8991         tape->pos[i].action[j] = MV_NONE;
8992
8993         if (tape->player_participates[j])
8994           tape->pos[i].action[j] = getFile8Bit(file);
8995       }
8996     }
8997
8998     if (tape->use_mouse_actions)
8999     {
9000       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9001       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9002       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9003     }
9004
9005     tape->pos[i].delay = getFile8Bit(file);
9006
9007     if (tape->file_version == FILE_VERSION_1_0)
9008     {
9009       // eliminate possible diagonal moves in old tapes
9010       // this is only for backward compatibility
9011
9012       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9013       byte action = tape->pos[i].action[0];
9014       int k, num_moves = 0;
9015
9016       for (k = 0; k < 4; k++)
9017       {
9018         if (action & joy_dir[k])
9019         {
9020           tape->pos[i + num_moves].action[0] = joy_dir[k];
9021           if (num_moves > 0)
9022             tape->pos[i + num_moves].delay = 0;
9023           num_moves++;
9024         }
9025       }
9026
9027       if (num_moves > 1)
9028       {
9029         num_moves--;
9030         i += num_moves;
9031         tape->length += num_moves;
9032       }
9033     }
9034     else if (tape->file_version < FILE_VERSION_2_0)
9035     {
9036       // convert pre-2.0 tapes to new tape format
9037
9038       if (tape->pos[i].delay > 1)
9039       {
9040         // action part
9041         tape->pos[i + 1] = tape->pos[i];
9042         tape->pos[i + 1].delay = 1;
9043
9044         // delay part
9045         for (j = 0; j < MAX_PLAYERS; j++)
9046           tape->pos[i].action[j] = MV_NONE;
9047         tape->pos[i].delay--;
9048
9049         i++;
9050         tape->length++;
9051       }
9052     }
9053
9054     if (checkEndOfFile(file))
9055       break;
9056   }
9057
9058   if (i != tape->length)
9059     chunk_size = tape_pos_size * i;
9060
9061   return chunk_size;
9062 }
9063
9064 static void LoadTape_SokobanSolution(char *filename)
9065 {
9066   File *file;
9067   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9068
9069   if (!(file = openFile(filename, MODE_READ)))
9070   {
9071     tape.no_valid_file = TRUE;
9072
9073     return;
9074   }
9075
9076   while (!checkEndOfFile(file))
9077   {
9078     unsigned char c = getByteFromFile(file);
9079
9080     if (checkEndOfFile(file))
9081       break;
9082
9083     switch (c)
9084     {
9085       case 'u':
9086       case 'U':
9087         tape.pos[tape.length].action[0] = MV_UP;
9088         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9089         tape.length++;
9090         break;
9091
9092       case 'd':
9093       case 'D':
9094         tape.pos[tape.length].action[0] = MV_DOWN;
9095         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9096         tape.length++;
9097         break;
9098
9099       case 'l':
9100       case 'L':
9101         tape.pos[tape.length].action[0] = MV_LEFT;
9102         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9103         tape.length++;
9104         break;
9105
9106       case 'r':
9107       case 'R':
9108         tape.pos[tape.length].action[0] = MV_RIGHT;
9109         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9110         tape.length++;
9111         break;
9112
9113       case '\n':
9114       case '\r':
9115       case '\t':
9116       case ' ':
9117         // ignore white-space characters
9118         break;
9119
9120       default:
9121         tape.no_valid_file = TRUE;
9122
9123         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9124
9125         break;
9126     }
9127   }
9128
9129   closeFile(file);
9130
9131   if (tape.no_valid_file)
9132     return;
9133
9134   tape.length_frames  = GetTapeLengthFrames();
9135   tape.length_seconds = GetTapeLengthSeconds();
9136 }
9137
9138 void LoadTapeFromFilename(char *filename)
9139 {
9140   char cookie[MAX_LINE_LEN];
9141   char chunk_name[CHUNK_ID_LEN + 1];
9142   File *file;
9143   int chunk_size;
9144
9145   // always start with reliable default values
9146   setTapeInfoToDefaults();
9147
9148   if (strSuffix(filename, ".sln"))
9149   {
9150     LoadTape_SokobanSolution(filename);
9151
9152     return;
9153   }
9154
9155   if (!(file = openFile(filename, MODE_READ)))
9156   {
9157     tape.no_valid_file = TRUE;
9158
9159     return;
9160   }
9161
9162   getFileChunkBE(file, chunk_name, NULL);
9163   if (strEqual(chunk_name, "RND1"))
9164   {
9165     getFile32BitBE(file);               // not used
9166
9167     getFileChunkBE(file, chunk_name, NULL);
9168     if (!strEqual(chunk_name, "TAPE"))
9169     {
9170       tape.no_valid_file = TRUE;
9171
9172       Warn("unknown format of tape file '%s'", filename);
9173
9174       closeFile(file);
9175
9176       return;
9177     }
9178   }
9179   else  // check for pre-2.0 file format with cookie string
9180   {
9181     strcpy(cookie, chunk_name);
9182     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9183       cookie[4] = '\0';
9184     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9185       cookie[strlen(cookie) - 1] = '\0';
9186
9187     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9188     {
9189       tape.no_valid_file = TRUE;
9190
9191       Warn("unknown format of tape file '%s'", filename);
9192
9193       closeFile(file);
9194
9195       return;
9196     }
9197
9198     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9199     {
9200       tape.no_valid_file = TRUE;
9201
9202       Warn("unsupported version of tape file '%s'", filename);
9203
9204       closeFile(file);
9205
9206       return;
9207     }
9208
9209     // pre-2.0 tape files have no game version, so use file version here
9210     tape.game_version = tape.file_version;
9211   }
9212
9213   if (tape.file_version < FILE_VERSION_1_2)
9214   {
9215     // tape files from versions before 1.2.0 without chunk structure
9216     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9217     LoadTape_BODY(file, 2 * tape.length,      &tape);
9218   }
9219   else
9220   {
9221     static struct
9222     {
9223       char *name;
9224       int size;
9225       int (*loader)(File *, int, struct TapeInfo *);
9226     }
9227     chunk_info[] =
9228     {
9229       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9230       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9231       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9232       { "INFO", -1,                     LoadTape_INFO },
9233       { "BODY", -1,                     LoadTape_BODY },
9234       {  NULL,  0,                      NULL }
9235     };
9236
9237     while (getFileChunkBE(file, chunk_name, &chunk_size))
9238     {
9239       int i = 0;
9240
9241       while (chunk_info[i].name != NULL &&
9242              !strEqual(chunk_name, chunk_info[i].name))
9243         i++;
9244
9245       if (chunk_info[i].name == NULL)
9246       {
9247         Warn("unknown chunk '%s' in tape file '%s'",
9248               chunk_name, filename);
9249
9250         ReadUnusedBytesFromFile(file, chunk_size);
9251       }
9252       else if (chunk_info[i].size != -1 &&
9253                chunk_info[i].size != chunk_size)
9254       {
9255         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9256               chunk_size, chunk_name, filename);
9257
9258         ReadUnusedBytesFromFile(file, chunk_size);
9259       }
9260       else
9261       {
9262         // call function to load this tape chunk
9263         int chunk_size_expected =
9264           (chunk_info[i].loader)(file, chunk_size, &tape);
9265
9266         // the size of some chunks cannot be checked before reading other
9267         // chunks first (like "HEAD" and "BODY") that contain some header
9268         // information, so check them here
9269         if (chunk_size_expected != chunk_size)
9270         {
9271           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9272                 chunk_size, chunk_name, filename);
9273         }
9274       }
9275     }
9276   }
9277
9278   closeFile(file);
9279
9280   tape.length_frames  = GetTapeLengthFrames();
9281   tape.length_seconds = GetTapeLengthSeconds();
9282
9283 #if 0
9284   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9285         tape.file_version);
9286   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9287         tape.game_version);
9288   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9289         tape.engine_version);
9290 #endif
9291 }
9292
9293 void LoadTape(int nr)
9294 {
9295   char *filename = getTapeFilename(nr);
9296
9297   LoadTapeFromFilename(filename);
9298 }
9299
9300 void LoadSolutionTape(int nr)
9301 {
9302   char *filename = getSolutionTapeFilename(nr);
9303
9304   LoadTapeFromFilename(filename);
9305
9306   if (TAPE_IS_EMPTY(tape))
9307   {
9308     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9309         level.native_bd_level->replay != NULL)
9310       CopyNativeTape_BD_to_RND(&level);
9311     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9312         level.native_sp_level->demo.is_available)
9313       CopyNativeTape_SP_to_RND(&level);
9314   }
9315 }
9316
9317 void LoadScoreTape(char *score_tape_basename, int nr)
9318 {
9319   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9320
9321   LoadTapeFromFilename(filename);
9322 }
9323
9324 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9325 {
9326   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9327
9328   LoadTapeFromFilename(filename);
9329 }
9330
9331 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9332 {
9333   // chunk required for team mode tapes with non-default screen size
9334   return (tape->num_participating_players > 1 &&
9335           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9336            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9337 }
9338
9339 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9340 {
9341   putFileVersion(file, tape->file_version);
9342   putFileVersion(file, tape->game_version);
9343 }
9344
9345 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9346 {
9347   int i;
9348   byte store_participating_players = 0;
9349
9350   // set bits for participating players for compact storage
9351   for (i = 0; i < MAX_PLAYERS; i++)
9352     if (tape->player_participates[i])
9353       store_participating_players |= (1 << i);
9354
9355   putFile32BitBE(file, tape->random_seed);
9356   putFile32BitBE(file, tape->date);
9357   putFile32BitBE(file, tape->length);
9358
9359   putFile8Bit(file, store_participating_players);
9360
9361   putFile8Bit(file, getTapeActionValue(tape));
9362
9363   putFile8Bit(file, tape->property_bits);
9364   putFile8Bit(file, tape->solved);
9365
9366   putFileVersion(file, tape->engine_version);
9367 }
9368
9369 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9370 {
9371   putFile8Bit(file, tape->scr_fieldx);
9372   putFile8Bit(file, tape->scr_fieldy);
9373 }
9374
9375 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9376 {
9377   int level_identifier_size = strlen(tape->level_identifier) + 1;
9378   int i;
9379
9380   putFile16BitBE(file, level_identifier_size);
9381
9382   for (i = 0; i < level_identifier_size; i++)
9383     putFile8Bit(file, tape->level_identifier[i]);
9384
9385   putFile16BitBE(file, tape->level_nr);
9386 }
9387
9388 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9389 {
9390   int i, j;
9391
9392   for (i = 0; i < tape->length; i++)
9393   {
9394     if (tape->use_key_actions)
9395     {
9396       for (j = 0; j < MAX_PLAYERS; j++)
9397         if (tape->player_participates[j])
9398           putFile8Bit(file, tape->pos[i].action[j]);
9399     }
9400
9401     if (tape->use_mouse_actions)
9402     {
9403       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9404       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9405       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9406     }
9407
9408     putFile8Bit(file, tape->pos[i].delay);
9409   }
9410 }
9411
9412 void SaveTapeToFilename(char *filename)
9413 {
9414   FILE *file;
9415   int tape_pos_size;
9416   int info_chunk_size;
9417   int body_chunk_size;
9418
9419   if (!(file = fopen(filename, MODE_WRITE)))
9420   {
9421     Warn("cannot save level recording file '%s'", filename);
9422
9423     return;
9424   }
9425
9426   tape_pos_size = getTapePosSize(&tape);
9427
9428   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9429   body_chunk_size = tape_pos_size * tape.length;
9430
9431   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9432   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9433
9434   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9435   SaveTape_VERS(file, &tape);
9436
9437   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9438   SaveTape_HEAD(file, &tape);
9439
9440   if (checkSaveTape_SCRN(&tape))
9441   {
9442     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9443     SaveTape_SCRN(file, &tape);
9444   }
9445
9446   putFileChunkBE(file, "INFO", info_chunk_size);
9447   SaveTape_INFO(file, &tape);
9448
9449   putFileChunkBE(file, "BODY", body_chunk_size);
9450   SaveTape_BODY(file, &tape);
9451
9452   fclose(file);
9453
9454   SetFilePermissions(filename, PERMS_PRIVATE);
9455 }
9456
9457 static void SaveTapeExt(char *filename)
9458 {
9459   int i;
9460
9461   tape.file_version = FILE_VERSION_ACTUAL;
9462   tape.game_version = GAME_VERSION_ACTUAL;
9463
9464   tape.num_participating_players = 0;
9465
9466   // count number of participating players
9467   for (i = 0; i < MAX_PLAYERS; i++)
9468     if (tape.player_participates[i])
9469       tape.num_participating_players++;
9470
9471   SaveTapeToFilename(filename);
9472
9473   tape.changed = FALSE;
9474 }
9475
9476 void SaveTape(int nr)
9477 {
9478   char *filename = getTapeFilename(nr);
9479
9480   InitTapeDirectory(leveldir_current->subdir);
9481
9482   SaveTapeExt(filename);
9483 }
9484
9485 void SaveScoreTape(int nr)
9486 {
9487   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9488
9489   // used instead of "leveldir_current->subdir" (for network games)
9490   InitScoreTapeDirectory(levelset.identifier, nr);
9491
9492   SaveTapeExt(filename);
9493 }
9494
9495 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9496                                   unsigned int req_state_added)
9497 {
9498   char *filename = getTapeFilename(nr);
9499   boolean new_tape = !fileExists(filename);
9500   boolean tape_saved = FALSE;
9501
9502   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9503   {
9504     SaveTape(nr);
9505
9506     if (new_tape)
9507       Request(msg_saved, REQ_CONFIRM | req_state_added);
9508
9509     tape_saved = TRUE;
9510   }
9511
9512   return tape_saved;
9513 }
9514
9515 boolean SaveTapeChecked(int nr)
9516 {
9517   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9518 }
9519
9520 boolean SaveTapeChecked_LevelSolved(int nr)
9521 {
9522   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9523                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9524 }
9525
9526 void DumpTape(struct TapeInfo *tape)
9527 {
9528   int tape_frame_counter;
9529   int i, j;
9530
9531   if (tape->no_valid_file)
9532   {
9533     Warn("cannot dump -- no valid tape file found");
9534
9535     return;
9536   }
9537
9538   PrintLine("-", 79);
9539
9540   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9541         tape->level_nr, tape->file_version, tape->game_version);
9542   Print("                  (effective engine version %08d)\n",
9543         tape->engine_version);
9544   Print("Level series identifier: '%s'\n", tape->level_identifier);
9545
9546   Print("Solution tape: %s\n",
9547         tape->solved ? "yes" :
9548         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9549
9550   Print("Special tape properties: ");
9551   if (tape->property_bits == TAPE_PROPERTY_NONE)
9552     Print("[none]");
9553   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9554     Print("[em_random_bug]");
9555   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9556     Print("[game_speed]");
9557   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9558     Print("[pause]");
9559   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9560     Print("[single_step]");
9561   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9562     Print("[snapshot]");
9563   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9564     Print("[replayed]");
9565   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9566     Print("[tas_keys]");
9567   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9568     Print("[small_graphics]");
9569   Print("\n");
9570
9571   int year2 = tape->date / 10000;
9572   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9573   int month_index_raw = (tape->date / 100) % 100;
9574   int month_index = month_index_raw % 12;       // prevent invalid index
9575   int month = month_index + 1;
9576   int day = tape->date % 100;
9577
9578   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9579
9580   PrintLine("-", 79);
9581
9582   tape_frame_counter = 0;
9583
9584   for (i = 0; i < tape->length; i++)
9585   {
9586     if (i >= MAX_TAPE_LEN)
9587       break;
9588
9589     Print("%04d: ", i);
9590
9591     for (j = 0; j < MAX_PLAYERS; j++)
9592     {
9593       if (tape->player_participates[j])
9594       {
9595         int action = tape->pos[i].action[j];
9596
9597         Print("%d:%02x ", j, action);
9598         Print("[%c%c%c%c|%c%c] - ",
9599               (action & JOY_LEFT ? '<' : ' '),
9600               (action & JOY_RIGHT ? '>' : ' '),
9601               (action & JOY_UP ? '^' : ' '),
9602               (action & JOY_DOWN ? 'v' : ' '),
9603               (action & JOY_BUTTON_1 ? '1' : ' '),
9604               (action & JOY_BUTTON_2 ? '2' : ' '));
9605       }
9606     }
9607
9608     Print("(%03d) ", tape->pos[i].delay);
9609     Print("[%05d]\n", tape_frame_counter);
9610
9611     tape_frame_counter += tape->pos[i].delay;
9612   }
9613
9614   PrintLine("-", 79);
9615 }
9616
9617 void DumpTapes(void)
9618 {
9619   static LevelDirTree *dumptape_leveldir = NULL;
9620
9621   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9622                                                 global.dumptape_leveldir);
9623
9624   if (dumptape_leveldir == NULL)
9625     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9626
9627   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9628       global.dumptape_level_nr > dumptape_leveldir->last_level)
9629     Fail("no such level number: %d", global.dumptape_level_nr);
9630
9631   leveldir_current = dumptape_leveldir;
9632
9633   if (options.mytapes)
9634     LoadTape(global.dumptape_level_nr);
9635   else
9636     LoadSolutionTape(global.dumptape_level_nr);
9637
9638   DumpTape(&tape);
9639
9640   CloseAllAndExit(0);
9641 }
9642
9643
9644 // ============================================================================
9645 // score file functions
9646 // ============================================================================
9647
9648 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9649 {
9650   int i;
9651
9652   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9653   {
9654     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9655     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9656     scores->entry[i].score = 0;
9657     scores->entry[i].time = 0;
9658
9659     scores->entry[i].id = -1;
9660     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9661     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9662     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9663     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9664     strcpy(scores->entry[i].country_code, "??");
9665   }
9666
9667   scores->num_entries = 0;
9668   scores->last_added = -1;
9669   scores->last_added_local = -1;
9670
9671   scores->updated = FALSE;
9672   scores->uploaded = FALSE;
9673   scores->tape_downloaded = FALSE;
9674   scores->force_last_added = FALSE;
9675
9676   // The following values are intentionally not reset here:
9677   // - last_level_nr
9678   // - last_entry_nr
9679   // - next_level_nr
9680   // - continue_playing
9681   // - continue_on_return
9682 }
9683
9684 static void setScoreInfoToDefaults(void)
9685 {
9686   setScoreInfoToDefaultsExt(&scores);
9687 }
9688
9689 static void setServerScoreInfoToDefaults(void)
9690 {
9691   setScoreInfoToDefaultsExt(&server_scores);
9692 }
9693
9694 static void LoadScore_OLD(int nr)
9695 {
9696   int i;
9697   char *filename = getScoreFilename(nr);
9698   char cookie[MAX_LINE_LEN];
9699   char line[MAX_LINE_LEN];
9700   char *line_ptr;
9701   FILE *file;
9702
9703   if (!(file = fopen(filename, MODE_READ)))
9704     return;
9705
9706   // check file identifier
9707   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9708     cookie[0] = '\0';
9709   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9710     cookie[strlen(cookie) - 1] = '\0';
9711
9712   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9713   {
9714     Warn("unknown format of score file '%s'", filename);
9715
9716     fclose(file);
9717
9718     return;
9719   }
9720
9721   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9722   {
9723     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9724       Warn("fscanf() failed; %s", strerror(errno));
9725
9726     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9727       line[0] = '\0';
9728
9729     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9730       line[strlen(line) - 1] = '\0';
9731
9732     for (line_ptr = line; *line_ptr; line_ptr++)
9733     {
9734       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9735       {
9736         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9737         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9738         break;
9739       }
9740     }
9741   }
9742
9743   fclose(file);
9744 }
9745
9746 static void ConvertScore_OLD(void)
9747 {
9748   // only convert score to time for levels that rate playing time over score
9749   if (!level.rate_time_over_score)
9750     return;
9751
9752   // convert old score to playing time for score-less levels (like Supaplex)
9753   int time_final_max = 999;
9754   int i;
9755
9756   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9757   {
9758     int score = scores.entry[i].score;
9759
9760     if (score > 0 && score < time_final_max)
9761       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9762   }
9763 }
9764
9765 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9766 {
9767   scores->file_version = getFileVersion(file);
9768   scores->game_version = getFileVersion(file);
9769
9770   return chunk_size;
9771 }
9772
9773 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9774 {
9775   char *level_identifier = NULL;
9776   int level_identifier_size;
9777   int i;
9778
9779   level_identifier_size = getFile16BitBE(file);
9780
9781   level_identifier = checked_malloc(level_identifier_size);
9782
9783   for (i = 0; i < level_identifier_size; i++)
9784     level_identifier[i] = getFile8Bit(file);
9785
9786   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9787   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9788
9789   checked_free(level_identifier);
9790
9791   scores->level_nr = getFile16BitBE(file);
9792   scores->num_entries = getFile16BitBE(file);
9793
9794   chunk_size = 2 + level_identifier_size + 2 + 2;
9795
9796   return chunk_size;
9797 }
9798
9799 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9800 {
9801   int i, j;
9802
9803   for (i = 0; i < scores->num_entries; i++)
9804   {
9805     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9806       scores->entry[i].name[j] = getFile8Bit(file);
9807
9808     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9809   }
9810
9811   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9812
9813   return chunk_size;
9814 }
9815
9816 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9817 {
9818   int i;
9819
9820   for (i = 0; i < scores->num_entries; i++)
9821     scores->entry[i].score = getFile16BitBE(file);
9822
9823   chunk_size = scores->num_entries * 2;
9824
9825   return chunk_size;
9826 }
9827
9828 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9829 {
9830   int i;
9831
9832   for (i = 0; i < scores->num_entries; i++)
9833     scores->entry[i].score = getFile32BitBE(file);
9834
9835   chunk_size = scores->num_entries * 4;
9836
9837   return chunk_size;
9838 }
9839
9840 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9841 {
9842   int i;
9843
9844   for (i = 0; i < scores->num_entries; i++)
9845     scores->entry[i].time = getFile32BitBE(file);
9846
9847   chunk_size = scores->num_entries * 4;
9848
9849   return chunk_size;
9850 }
9851
9852 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9853 {
9854   int i, j;
9855
9856   for (i = 0; i < scores->num_entries; i++)
9857   {
9858     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9859       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9860
9861     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9862   }
9863
9864   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9865
9866   return chunk_size;
9867 }
9868
9869 void LoadScore(int nr)
9870 {
9871   char *filename = getScoreFilename(nr);
9872   char cookie[MAX_LINE_LEN];
9873   char chunk_name[CHUNK_ID_LEN + 1];
9874   int chunk_size;
9875   boolean old_score_file_format = FALSE;
9876   File *file;
9877
9878   // always start with reliable default values
9879   setScoreInfoToDefaults();
9880
9881   if (!(file = openFile(filename, MODE_READ)))
9882     return;
9883
9884   getFileChunkBE(file, chunk_name, NULL);
9885   if (strEqual(chunk_name, "RND1"))
9886   {
9887     getFile32BitBE(file);               // not used
9888
9889     getFileChunkBE(file, chunk_name, NULL);
9890     if (!strEqual(chunk_name, "SCOR"))
9891     {
9892       Warn("unknown format of score file '%s'", filename);
9893
9894       closeFile(file);
9895
9896       return;
9897     }
9898   }
9899   else  // check for old file format with cookie string
9900   {
9901     strcpy(cookie, chunk_name);
9902     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9903       cookie[4] = '\0';
9904     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9905       cookie[strlen(cookie) - 1] = '\0';
9906
9907     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9908     {
9909       Warn("unknown format of score file '%s'", filename);
9910
9911       closeFile(file);
9912
9913       return;
9914     }
9915
9916     old_score_file_format = TRUE;
9917   }
9918
9919   if (old_score_file_format)
9920   {
9921     // score files from versions before 4.2.4.0 without chunk structure
9922     LoadScore_OLD(nr);
9923
9924     // convert score to time, if possible (mainly for Supaplex levels)
9925     ConvertScore_OLD();
9926   }
9927   else
9928   {
9929     static struct
9930     {
9931       char *name;
9932       int size;
9933       int (*loader)(File *, int, struct ScoreInfo *);
9934     }
9935     chunk_info[] =
9936     {
9937       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9938       { "INFO", -1,                     LoadScore_INFO },
9939       { "NAME", -1,                     LoadScore_NAME },
9940       { "SCOR", -1,                     LoadScore_SCOR },
9941       { "SC4R", -1,                     LoadScore_SC4R },
9942       { "TIME", -1,                     LoadScore_TIME },
9943       { "TAPE", -1,                     LoadScore_TAPE },
9944
9945       {  NULL,  0,                      NULL }
9946     };
9947
9948     while (getFileChunkBE(file, chunk_name, &chunk_size))
9949     {
9950       int i = 0;
9951
9952       while (chunk_info[i].name != NULL &&
9953              !strEqual(chunk_name, chunk_info[i].name))
9954         i++;
9955
9956       if (chunk_info[i].name == NULL)
9957       {
9958         Warn("unknown chunk '%s' in score file '%s'",
9959               chunk_name, filename);
9960
9961         ReadUnusedBytesFromFile(file, chunk_size);
9962       }
9963       else if (chunk_info[i].size != -1 &&
9964                chunk_info[i].size != chunk_size)
9965       {
9966         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9967               chunk_size, chunk_name, filename);
9968
9969         ReadUnusedBytesFromFile(file, chunk_size);
9970       }
9971       else
9972       {
9973         // call function to load this score chunk
9974         int chunk_size_expected =
9975           (chunk_info[i].loader)(file, chunk_size, &scores);
9976
9977         // the size of some chunks cannot be checked before reading other
9978         // chunks first (like "HEAD" and "BODY") that contain some header
9979         // information, so check them here
9980         if (chunk_size_expected != chunk_size)
9981         {
9982           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9983                 chunk_size, chunk_name, filename);
9984         }
9985       }
9986     }
9987   }
9988
9989   closeFile(file);
9990 }
9991
9992 #if ENABLE_HISTORIC_CHUNKS
9993 void SaveScore_OLD(int nr)
9994 {
9995   int i;
9996   char *filename = getScoreFilename(nr);
9997   FILE *file;
9998
9999   // used instead of "leveldir_current->subdir" (for network games)
10000   InitScoreDirectory(levelset.identifier);
10001
10002   if (!(file = fopen(filename, MODE_WRITE)))
10003   {
10004     Warn("cannot save score for level %d", nr);
10005
10006     return;
10007   }
10008
10009   fprintf(file, "%s\n\n", SCORE_COOKIE);
10010
10011   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10012     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10013
10014   fclose(file);
10015
10016   SetFilePermissions(filename, PERMS_PRIVATE);
10017 }
10018 #endif
10019
10020 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10021 {
10022   putFileVersion(file, scores->file_version);
10023   putFileVersion(file, scores->game_version);
10024 }
10025
10026 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10027 {
10028   int level_identifier_size = strlen(scores->level_identifier) + 1;
10029   int i;
10030
10031   putFile16BitBE(file, level_identifier_size);
10032
10033   for (i = 0; i < level_identifier_size; i++)
10034     putFile8Bit(file, scores->level_identifier[i]);
10035
10036   putFile16BitBE(file, scores->level_nr);
10037   putFile16BitBE(file, scores->num_entries);
10038 }
10039
10040 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10041 {
10042   int i, j;
10043
10044   for (i = 0; i < scores->num_entries; i++)
10045   {
10046     int name_size = strlen(scores->entry[i].name);
10047
10048     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10049       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10050   }
10051 }
10052
10053 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10054 {
10055   int i;
10056
10057   for (i = 0; i < scores->num_entries; i++)
10058     putFile16BitBE(file, scores->entry[i].score);
10059 }
10060
10061 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10062 {
10063   int i;
10064
10065   for (i = 0; i < scores->num_entries; i++)
10066     putFile32BitBE(file, scores->entry[i].score);
10067 }
10068
10069 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10070 {
10071   int i;
10072
10073   for (i = 0; i < scores->num_entries; i++)
10074     putFile32BitBE(file, scores->entry[i].time);
10075 }
10076
10077 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10078 {
10079   int i, j;
10080
10081   for (i = 0; i < scores->num_entries; i++)
10082   {
10083     int size = strlen(scores->entry[i].tape_basename);
10084
10085     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10086       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10087   }
10088 }
10089
10090 static void SaveScoreToFilename(char *filename)
10091 {
10092   FILE *file;
10093   int info_chunk_size;
10094   int name_chunk_size;
10095   int scor_chunk_size;
10096   int sc4r_chunk_size;
10097   int time_chunk_size;
10098   int tape_chunk_size;
10099   boolean has_large_score_values;
10100   int i;
10101
10102   if (!(file = fopen(filename, MODE_WRITE)))
10103   {
10104     Warn("cannot save score file '%s'", filename);
10105
10106     return;
10107   }
10108
10109   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10110   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10111   scor_chunk_size = scores.num_entries * 2;
10112   sc4r_chunk_size = scores.num_entries * 4;
10113   time_chunk_size = scores.num_entries * 4;
10114   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10115
10116   has_large_score_values = FALSE;
10117   for (i = 0; i < scores.num_entries; i++)
10118     if (scores.entry[i].score > 0xffff)
10119       has_large_score_values = TRUE;
10120
10121   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10122   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10123
10124   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10125   SaveScore_VERS(file, &scores);
10126
10127   putFileChunkBE(file, "INFO", info_chunk_size);
10128   SaveScore_INFO(file, &scores);
10129
10130   putFileChunkBE(file, "NAME", name_chunk_size);
10131   SaveScore_NAME(file, &scores);
10132
10133   if (has_large_score_values)
10134   {
10135     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10136     SaveScore_SC4R(file, &scores);
10137   }
10138   else
10139   {
10140     putFileChunkBE(file, "SCOR", scor_chunk_size);
10141     SaveScore_SCOR(file, &scores);
10142   }
10143
10144   putFileChunkBE(file, "TIME", time_chunk_size);
10145   SaveScore_TIME(file, &scores);
10146
10147   putFileChunkBE(file, "TAPE", tape_chunk_size);
10148   SaveScore_TAPE(file, &scores);
10149
10150   fclose(file);
10151
10152   SetFilePermissions(filename, PERMS_PRIVATE);
10153 }
10154
10155 void SaveScore(int nr)
10156 {
10157   char *filename = getScoreFilename(nr);
10158   int i;
10159
10160   // used instead of "leveldir_current->subdir" (for network games)
10161   InitScoreDirectory(levelset.identifier);
10162
10163   scores.file_version = FILE_VERSION_ACTUAL;
10164   scores.game_version = GAME_VERSION_ACTUAL;
10165
10166   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10167   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10168   scores.level_nr = level_nr;
10169
10170   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10171     if (scores.entry[i].score == 0 &&
10172         scores.entry[i].time == 0 &&
10173         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10174       break;
10175
10176   scores.num_entries = i;
10177
10178   if (scores.num_entries == 0)
10179     return;
10180
10181   SaveScoreToFilename(filename);
10182 }
10183
10184 static void LoadServerScoreFromCache(int nr)
10185 {
10186   struct ScoreEntry score_entry;
10187   struct
10188   {
10189     void *value;
10190     boolean is_string;
10191     int string_size;
10192   }
10193   score_mapping[] =
10194   {
10195     { &score_entry.score,               FALSE,  0                       },
10196     { &score_entry.time,                FALSE,  0                       },
10197     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10198     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10199     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10200     { &score_entry.id,                  FALSE,  0                       },
10201     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10202     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10203     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10204     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10205
10206     { NULL,                             FALSE,  0                       }
10207   };
10208   char *filename = getScoreCacheFilename(nr);
10209   SetupFileHash *score_hash = loadSetupFileHash(filename);
10210   int i, j;
10211
10212   server_scores.num_entries = 0;
10213
10214   if (score_hash == NULL)
10215     return;
10216
10217   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10218   {
10219     score_entry = server_scores.entry[i];
10220
10221     for (j = 0; score_mapping[j].value != NULL; j++)
10222     {
10223       char token[10];
10224
10225       sprintf(token, "%02d.%d", i, j);
10226
10227       char *value = getHashEntry(score_hash, token);
10228
10229       if (value == NULL)
10230         continue;
10231
10232       if (score_mapping[j].is_string)
10233       {
10234         char *score_value = (char *)score_mapping[j].value;
10235         int value_size = score_mapping[j].string_size;
10236
10237         strncpy(score_value, value, value_size);
10238         score_value[value_size] = '\0';
10239       }
10240       else
10241       {
10242         int *score_value = (int *)score_mapping[j].value;
10243
10244         *score_value = atoi(value);
10245       }
10246
10247       server_scores.num_entries = i + 1;
10248     }
10249
10250     server_scores.entry[i] = score_entry;
10251   }
10252
10253   freeSetupFileHash(score_hash);
10254 }
10255
10256 void LoadServerScore(int nr, boolean download_score)
10257 {
10258   if (!setup.use_api_server)
10259     return;
10260
10261   // always start with reliable default values
10262   setServerScoreInfoToDefaults();
10263
10264   // 1st step: load server scores from cache file (which may not exist)
10265   // (this should prevent reading it while the thread is writing to it)
10266   LoadServerScoreFromCache(nr);
10267
10268   if (download_score && runtime.use_api_server)
10269   {
10270     // 2nd step: download server scores from score server to cache file
10271     // (as thread, as it might time out if the server is not reachable)
10272     ApiGetScoreAsThread(nr);
10273   }
10274 }
10275
10276 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10277 {
10278   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10279
10280   // if score tape not uploaded, ask for uploading missing tapes later
10281   if (!setup.has_remaining_tapes)
10282     setup.ask_for_remaining_tapes = TRUE;
10283
10284   setup.provide_uploading_tapes = TRUE;
10285   setup.has_remaining_tapes = TRUE;
10286
10287   SaveSetup_ServerSetup();
10288 }
10289
10290 void SaveServerScore(int nr, boolean tape_saved)
10291 {
10292   if (!runtime.use_api_server)
10293   {
10294     PrepareScoreTapesForUpload(leveldir_current->subdir);
10295
10296     return;
10297   }
10298
10299   ApiAddScoreAsThread(nr, tape_saved, NULL);
10300 }
10301
10302 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10303                              char *score_tape_filename)
10304 {
10305   if (!runtime.use_api_server)
10306     return;
10307
10308   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10309 }
10310
10311 void LoadLocalAndServerScore(int nr, boolean download_score)
10312 {
10313   int last_added_local = scores.last_added_local;
10314   boolean force_last_added = scores.force_last_added;
10315
10316   // needed if only showing server scores
10317   setScoreInfoToDefaults();
10318
10319   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10320     LoadScore(nr);
10321
10322   // restore last added local score entry (before merging server scores)
10323   scores.last_added = scores.last_added_local = last_added_local;
10324
10325   if (setup.use_api_server &&
10326       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10327   {
10328     // load server scores from cache file and trigger update from server
10329     LoadServerScore(nr, download_score);
10330
10331     // merge local scores with scores from server
10332     MergeServerScore();
10333   }
10334
10335   if (force_last_added)
10336     scores.force_last_added = force_last_added;
10337 }
10338
10339
10340 // ============================================================================
10341 // setup file functions
10342 // ============================================================================
10343
10344 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10345
10346
10347 static struct TokenInfo global_setup_tokens[] =
10348 {
10349   {
10350     TYPE_STRING,
10351     &setup.player_name,                         "player_name"
10352   },
10353   {
10354     TYPE_SWITCH,
10355     &setup.multiple_users,                      "multiple_users"
10356   },
10357   {
10358     TYPE_SWITCH,
10359     &setup.sound,                               "sound"
10360   },
10361   {
10362     TYPE_SWITCH,
10363     &setup.sound_loops,                         "repeating_sound_loops"
10364   },
10365   {
10366     TYPE_SWITCH,
10367     &setup.sound_music,                         "background_music"
10368   },
10369   {
10370     TYPE_SWITCH,
10371     &setup.sound_simple,                        "simple_sound_effects"
10372   },
10373   {
10374     TYPE_SWITCH,
10375     &setup.toons,                               "toons"
10376   },
10377   {
10378     TYPE_SWITCH,
10379     &setup.global_animations,                   "global_animations"
10380   },
10381   {
10382     TYPE_SWITCH,
10383     &setup.scroll_delay,                        "scroll_delay"
10384   },
10385   {
10386     TYPE_SWITCH,
10387     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10388   },
10389   {
10390     TYPE_INTEGER,
10391     &setup.scroll_delay_value,                  "scroll_delay_value"
10392   },
10393   {
10394     TYPE_STRING,
10395     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10396   },
10397   {
10398     TYPE_INTEGER,
10399     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10400   },
10401   {
10402     TYPE_SWITCH,
10403     &setup.fade_screens,                        "fade_screens"
10404   },
10405   {
10406     TYPE_SWITCH,
10407     &setup.autorecord,                          "automatic_tape_recording"
10408   },
10409   {
10410     TYPE_SWITCH,
10411     &setup.autorecord_after_replay,             "autorecord_after_replay"
10412   },
10413   {
10414     TYPE_SWITCH,
10415     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10416   },
10417   {
10418     TYPE_SWITCH,
10419     &setup.show_titlescreen,                    "show_titlescreen"
10420   },
10421   {
10422     TYPE_SWITCH,
10423     &setup.quick_doors,                         "quick_doors"
10424   },
10425   {
10426     TYPE_SWITCH,
10427     &setup.team_mode,                           "team_mode"
10428   },
10429   {
10430     TYPE_SWITCH,
10431     &setup.handicap,                            "handicap"
10432   },
10433   {
10434     TYPE_SWITCH,
10435     &setup.skip_levels,                         "skip_levels"
10436   },
10437   {
10438     TYPE_SWITCH,
10439     &setup.increment_levels,                    "increment_levels"
10440   },
10441   {
10442     TYPE_SWITCH,
10443     &setup.auto_play_next_level,                "auto_play_next_level"
10444   },
10445   {
10446     TYPE_SWITCH,
10447     &setup.count_score_after_game,              "count_score_after_game"
10448   },
10449   {
10450     TYPE_SWITCH,
10451     &setup.show_scores_after_game,              "show_scores_after_game"
10452   },
10453   {
10454     TYPE_SWITCH,
10455     &setup.time_limit,                          "time_limit"
10456   },
10457   {
10458     TYPE_SWITCH,
10459     &setup.fullscreen,                          "fullscreen"
10460   },
10461   {
10462     TYPE_INTEGER,
10463     &setup.window_scaling_percent,              "window_scaling_percent"
10464   },
10465   {
10466     TYPE_STRING,
10467     &setup.window_scaling_quality,              "window_scaling_quality"
10468   },
10469   {
10470     TYPE_STRING,
10471     &setup.screen_rendering_mode,               "screen_rendering_mode"
10472   },
10473   {
10474     TYPE_STRING,
10475     &setup.vsync_mode,                          "vsync_mode"
10476   },
10477   {
10478     TYPE_SWITCH,
10479     &setup.ask_on_escape,                       "ask_on_escape"
10480   },
10481   {
10482     TYPE_SWITCH,
10483     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10484   },
10485   {
10486     TYPE_SWITCH,
10487     &setup.ask_on_game_over,                    "ask_on_game_over"
10488   },
10489   {
10490     TYPE_SWITCH,
10491     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10492   },
10493   {
10494     TYPE_SWITCH,
10495     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10496   },
10497   {
10498     TYPE_SWITCH,
10499     &setup.quick_switch,                        "quick_player_switch"
10500   },
10501   {
10502     TYPE_SWITCH,
10503     &setup.input_on_focus,                      "input_on_focus"
10504   },
10505   {
10506     TYPE_SWITCH,
10507     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10508   },
10509   {
10510     TYPE_SWITCH,
10511     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10512   },
10513   {
10514     TYPE_SWITCH,
10515     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10516   },
10517   {
10518     TYPE_SWITCH,
10519     &setup.game_speed_extended,                 "game_speed_extended"
10520   },
10521   {
10522     TYPE_INTEGER,
10523     &setup.game_frame_delay,                    "game_frame_delay"
10524   },
10525   {
10526     TYPE_SWITCH,
10527     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10528   },
10529   {
10530     TYPE_SWITCH,
10531     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10532   },
10533   {
10534     TYPE_SWITCH,
10535     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10536   },
10537   {
10538     TYPE_SWITCH3,
10539     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10540   },
10541   {
10542     TYPE_SWITCH,
10543     &setup.sp_show_border_elements,             "sp_show_border_elements"
10544   },
10545   {
10546     TYPE_SWITCH,
10547     &setup.small_game_graphics,                 "small_game_graphics"
10548   },
10549   {
10550     TYPE_SWITCH,
10551     &setup.show_load_save_buttons,              "show_load_save_buttons"
10552   },
10553   {
10554     TYPE_SWITCH,
10555     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10556   },
10557   {
10558     TYPE_STRING,
10559     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10560   },
10561   {
10562     TYPE_STRING,
10563     &setup.graphics_set,                        "graphics_set"
10564   },
10565   {
10566     TYPE_STRING,
10567     &setup.sounds_set,                          "sounds_set"
10568   },
10569   {
10570     TYPE_STRING,
10571     &setup.music_set,                           "music_set"
10572   },
10573   {
10574     TYPE_SWITCH3,
10575     &setup.override_level_graphics,             "override_level_graphics"
10576   },
10577   {
10578     TYPE_SWITCH3,
10579     &setup.override_level_sounds,               "override_level_sounds"
10580   },
10581   {
10582     TYPE_SWITCH3,
10583     &setup.override_level_music,                "override_level_music"
10584   },
10585   {
10586     TYPE_INTEGER,
10587     &setup.volume_simple,                       "volume_simple"
10588   },
10589   {
10590     TYPE_INTEGER,
10591     &setup.volume_loops,                        "volume_loops"
10592   },
10593   {
10594     TYPE_INTEGER,
10595     &setup.volume_music,                        "volume_music"
10596   },
10597   {
10598     TYPE_SWITCH,
10599     &setup.network_mode,                        "network_mode"
10600   },
10601   {
10602     TYPE_PLAYER,
10603     &setup.network_player_nr,                   "network_player"
10604   },
10605   {
10606     TYPE_STRING,
10607     &setup.network_server_hostname,             "network_server_hostname"
10608   },
10609   {
10610     TYPE_STRING,
10611     &setup.touch.control_type,                  "touch.control_type"
10612   },
10613   {
10614     TYPE_INTEGER,
10615     &setup.touch.move_distance,                 "touch.move_distance"
10616   },
10617   {
10618     TYPE_INTEGER,
10619     &setup.touch.drop_distance,                 "touch.drop_distance"
10620   },
10621   {
10622     TYPE_INTEGER,
10623     &setup.touch.transparency,                  "touch.transparency"
10624   },
10625   {
10626     TYPE_INTEGER,
10627     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10628   },
10629   {
10630     TYPE_INTEGER,
10631     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10632   },
10633   {
10634     TYPE_INTEGER,
10635     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10636   },
10637   {
10638     TYPE_INTEGER,
10639     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10640   },
10641   {
10642     TYPE_INTEGER,
10643     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10644   },
10645   {
10646     TYPE_INTEGER,
10647     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10648   },
10649   {
10650     TYPE_SWITCH,
10651     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10652   },
10653 };
10654
10655 static struct TokenInfo auto_setup_tokens[] =
10656 {
10657   {
10658     TYPE_INTEGER,
10659     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10660   },
10661 };
10662
10663 static struct TokenInfo server_setup_tokens[] =
10664 {
10665   {
10666     TYPE_STRING,
10667     &setup.player_uuid,                         "player_uuid"
10668   },
10669   {
10670     TYPE_INTEGER,
10671     &setup.player_version,                      "player_version"
10672   },
10673   {
10674     TYPE_SWITCH,
10675     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10676   },
10677   {
10678     TYPE_STRING,
10679     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10680   },
10681   {
10682     TYPE_STRING,
10683     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10684   },
10685   {
10686     TYPE_SWITCH,
10687     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10688   },
10689   {
10690     TYPE_SWITCH,
10691     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10692   },
10693   {
10694     TYPE_SWITCH,
10695     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10696   },
10697   {
10698     TYPE_SWITCH,
10699     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10700   },
10701   {
10702     TYPE_SWITCH,
10703     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10704   },
10705 };
10706
10707 static struct TokenInfo editor_setup_tokens[] =
10708 {
10709   {
10710     TYPE_SWITCH,
10711     &setup.editor.el_classic,                   "editor.el_classic"
10712   },
10713   {
10714     TYPE_SWITCH,
10715     &setup.editor.el_custom,                    "editor.el_custom"
10716   },
10717   {
10718     TYPE_SWITCH,
10719     &setup.editor.el_user_defined,              "editor.el_user_defined"
10720   },
10721   {
10722     TYPE_SWITCH,
10723     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10724   },
10725   {
10726     TYPE_SWITCH,
10727     &setup.editor.el_headlines,                 "editor.el_headlines"
10728   },
10729   {
10730     TYPE_SWITCH,
10731     &setup.editor.show_element_token,           "editor.show_element_token"
10732   },
10733   {
10734     TYPE_SWITCH,
10735     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10736   },
10737 };
10738
10739 static struct TokenInfo editor_cascade_setup_tokens[] =
10740 {
10741   {
10742     TYPE_SWITCH,
10743     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10744   },
10745   {
10746     TYPE_SWITCH,
10747     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10748   },
10749   {
10750     TYPE_SWITCH,
10751     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10752   },
10753   {
10754     TYPE_SWITCH,
10755     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10756   },
10757   {
10758     TYPE_SWITCH,
10759     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10760   },
10761   {
10762     TYPE_SWITCH,
10763     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10764   },
10765   {
10766     TYPE_SWITCH,
10767     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10768   },
10769   {
10770     TYPE_SWITCH,
10771     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10772   },
10773   {
10774     TYPE_SWITCH,
10775     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10776   },
10777   {
10778     TYPE_SWITCH,
10779     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10780   },
10781   {
10782     TYPE_SWITCH,
10783     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10784   },
10785   {
10786     TYPE_SWITCH,
10787     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10788   },
10789   {
10790     TYPE_SWITCH,
10791     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10792   },
10793   {
10794     TYPE_SWITCH,
10795     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10796   },
10797   {
10798     TYPE_SWITCH,
10799     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10800   },
10801   {
10802     TYPE_SWITCH,
10803     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10804   },
10805   {
10806     TYPE_SWITCH,
10807     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10808   },
10809   {
10810     TYPE_SWITCH,
10811     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10812   },
10813   {
10814     TYPE_SWITCH,
10815     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10816   },
10817   {
10818     TYPE_SWITCH,
10819     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10820   },
10821 };
10822
10823 static struct TokenInfo shortcut_setup_tokens[] =
10824 {
10825   {
10826     TYPE_KEY_X11,
10827     &setup.shortcut.save_game,                  "shortcut.save_game"
10828   },
10829   {
10830     TYPE_KEY_X11,
10831     &setup.shortcut.load_game,                  "shortcut.load_game"
10832   },
10833   {
10834     TYPE_KEY_X11,
10835     &setup.shortcut.restart_game,               "shortcut.restart_game"
10836   },
10837   {
10838     TYPE_KEY_X11,
10839     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10840   },
10841   {
10842     TYPE_KEY_X11,
10843     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10844   },
10845   {
10846     TYPE_KEY_X11,
10847     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10848   },
10849   {
10850     TYPE_KEY_X11,
10851     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10852   },
10853   {
10854     TYPE_KEY_X11,
10855     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10856   },
10857   {
10858     TYPE_KEY_X11,
10859     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10860   },
10861   {
10862     TYPE_KEY_X11,
10863     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10864   },
10865   {
10866     TYPE_KEY_X11,
10867     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10868   },
10869   {
10870     TYPE_KEY_X11,
10871     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10872   },
10873   {
10874     TYPE_KEY_X11,
10875     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10876   },
10877   {
10878     TYPE_KEY_X11,
10879     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10880   },
10881   {
10882     TYPE_KEY_X11,
10883     &setup.shortcut.tape_record,                "shortcut.tape_record"
10884   },
10885   {
10886     TYPE_KEY_X11,
10887     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10888   },
10889   {
10890     TYPE_KEY_X11,
10891     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10892   },
10893   {
10894     TYPE_KEY_X11,
10895     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10896   },
10897   {
10898     TYPE_KEY_X11,
10899     &setup.shortcut.sound_music,                "shortcut.sound_music"
10900   },
10901   {
10902     TYPE_KEY_X11,
10903     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10904   },
10905   {
10906     TYPE_KEY_X11,
10907     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10908   },
10909   {
10910     TYPE_KEY_X11,
10911     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10912   },
10913   {
10914     TYPE_KEY_X11,
10915     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10916   },
10917 };
10918
10919 static struct SetupInputInfo setup_input;
10920 static struct TokenInfo player_setup_tokens[] =
10921 {
10922   {
10923     TYPE_BOOLEAN,
10924     &setup_input.use_joystick,                  ".use_joystick"
10925   },
10926   {
10927     TYPE_STRING,
10928     &setup_input.joy.device_name,               ".joy.device_name"
10929   },
10930   {
10931     TYPE_INTEGER,
10932     &setup_input.joy.xleft,                     ".joy.xleft"
10933   },
10934   {
10935     TYPE_INTEGER,
10936     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10937   },
10938   {
10939     TYPE_INTEGER,
10940     &setup_input.joy.xright,                    ".joy.xright"
10941   },
10942   {
10943     TYPE_INTEGER,
10944     &setup_input.joy.yupper,                    ".joy.yupper"
10945   },
10946   {
10947     TYPE_INTEGER,
10948     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10949   },
10950   {
10951     TYPE_INTEGER,
10952     &setup_input.joy.ylower,                    ".joy.ylower"
10953   },
10954   {
10955     TYPE_INTEGER,
10956     &setup_input.joy.snap,                      ".joy.snap_field"
10957   },
10958   {
10959     TYPE_INTEGER,
10960     &setup_input.joy.drop,                      ".joy.place_bomb"
10961   },
10962   {
10963     TYPE_KEY_X11,
10964     &setup_input.key.left,                      ".key.move_left"
10965   },
10966   {
10967     TYPE_KEY_X11,
10968     &setup_input.key.right,                     ".key.move_right"
10969   },
10970   {
10971     TYPE_KEY_X11,
10972     &setup_input.key.up,                        ".key.move_up"
10973   },
10974   {
10975     TYPE_KEY_X11,
10976     &setup_input.key.down,                      ".key.move_down"
10977   },
10978   {
10979     TYPE_KEY_X11,
10980     &setup_input.key.snap,                      ".key.snap_field"
10981   },
10982   {
10983     TYPE_KEY_X11,
10984     &setup_input.key.drop,                      ".key.place_bomb"
10985   },
10986 };
10987
10988 static struct TokenInfo system_setup_tokens[] =
10989 {
10990   {
10991     TYPE_STRING,
10992     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10993   },
10994   {
10995     TYPE_STRING,
10996     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10997   },
10998   {
10999     TYPE_STRING,
11000     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11001   },
11002   {
11003     TYPE_INTEGER,
11004     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11005   },
11006 };
11007
11008 static struct TokenInfo internal_setup_tokens[] =
11009 {
11010   {
11011     TYPE_STRING,
11012     &setup.internal.program_title,              "program_title"
11013   },
11014   {
11015     TYPE_STRING,
11016     &setup.internal.program_version,            "program_version"
11017   },
11018   {
11019     TYPE_STRING,
11020     &setup.internal.program_author,             "program_author"
11021   },
11022   {
11023     TYPE_STRING,
11024     &setup.internal.program_email,              "program_email"
11025   },
11026   {
11027     TYPE_STRING,
11028     &setup.internal.program_website,            "program_website"
11029   },
11030   {
11031     TYPE_STRING,
11032     &setup.internal.program_copyright,          "program_copyright"
11033   },
11034   {
11035     TYPE_STRING,
11036     &setup.internal.program_company,            "program_company"
11037   },
11038   {
11039     TYPE_STRING,
11040     &setup.internal.program_icon_file,          "program_icon_file"
11041   },
11042   {
11043     TYPE_STRING,
11044     &setup.internal.default_graphics_set,       "default_graphics_set"
11045   },
11046   {
11047     TYPE_STRING,
11048     &setup.internal.default_sounds_set,         "default_sounds_set"
11049   },
11050   {
11051     TYPE_STRING,
11052     &setup.internal.default_music_set,          "default_music_set"
11053   },
11054   {
11055     TYPE_STRING,
11056     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11057   },
11058   {
11059     TYPE_STRING,
11060     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11061   },
11062   {
11063     TYPE_STRING,
11064     &setup.internal.fallback_music_file,        "fallback_music_file"
11065   },
11066   {
11067     TYPE_STRING,
11068     &setup.internal.default_level_series,       "default_level_series"
11069   },
11070   {
11071     TYPE_INTEGER,
11072     &setup.internal.default_window_width,       "default_window_width"
11073   },
11074   {
11075     TYPE_INTEGER,
11076     &setup.internal.default_window_height,      "default_window_height"
11077   },
11078   {
11079     TYPE_BOOLEAN,
11080     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11081   },
11082   {
11083     TYPE_BOOLEAN,
11084     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11085   },
11086   {
11087     TYPE_BOOLEAN,
11088     &setup.internal.create_user_levelset,       "create_user_levelset"
11089   },
11090   {
11091     TYPE_BOOLEAN,
11092     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11093   },
11094   {
11095     TYPE_BOOLEAN,
11096     &setup.internal.menu_game,                  "menu_game"
11097   },
11098   {
11099     TYPE_BOOLEAN,
11100     &setup.internal.menu_engines,               "menu_engines"
11101   },
11102   {
11103     TYPE_BOOLEAN,
11104     &setup.internal.menu_editor,                "menu_editor"
11105   },
11106   {
11107     TYPE_BOOLEAN,
11108     &setup.internal.menu_graphics,              "menu_graphics"
11109   },
11110   {
11111     TYPE_BOOLEAN,
11112     &setup.internal.menu_sound,                 "menu_sound"
11113   },
11114   {
11115     TYPE_BOOLEAN,
11116     &setup.internal.menu_artwork,               "menu_artwork"
11117   },
11118   {
11119     TYPE_BOOLEAN,
11120     &setup.internal.menu_input,                 "menu_input"
11121   },
11122   {
11123     TYPE_BOOLEAN,
11124     &setup.internal.menu_touch,                 "menu_touch"
11125   },
11126   {
11127     TYPE_BOOLEAN,
11128     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11129   },
11130   {
11131     TYPE_BOOLEAN,
11132     &setup.internal.menu_exit,                  "menu_exit"
11133   },
11134   {
11135     TYPE_BOOLEAN,
11136     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11137   },
11138   {
11139     TYPE_BOOLEAN,
11140     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11141   },
11142   {
11143     TYPE_BOOLEAN,
11144     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11145   },
11146   {
11147     TYPE_BOOLEAN,
11148     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11149   },
11150   {
11151     TYPE_BOOLEAN,
11152     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11153   },
11154   {
11155     TYPE_BOOLEAN,
11156     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11157   },
11158   {
11159     TYPE_BOOLEAN,
11160     &setup.internal.info_title,                 "info_title"
11161   },
11162   {
11163     TYPE_BOOLEAN,
11164     &setup.internal.info_elements,              "info_elements"
11165   },
11166   {
11167     TYPE_BOOLEAN,
11168     &setup.internal.info_music,                 "info_music"
11169   },
11170   {
11171     TYPE_BOOLEAN,
11172     &setup.internal.info_credits,               "info_credits"
11173   },
11174   {
11175     TYPE_BOOLEAN,
11176     &setup.internal.info_program,               "info_program"
11177   },
11178   {
11179     TYPE_BOOLEAN,
11180     &setup.internal.info_version,               "info_version"
11181   },
11182   {
11183     TYPE_BOOLEAN,
11184     &setup.internal.info_levelset,              "info_levelset"
11185   },
11186   {
11187     TYPE_BOOLEAN,
11188     &setup.internal.info_exit,                  "info_exit"
11189   },
11190 };
11191
11192 static struct TokenInfo debug_setup_tokens[] =
11193 {
11194   {
11195     TYPE_INTEGER,
11196     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11197   },
11198   {
11199     TYPE_INTEGER,
11200     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11201   },
11202   {
11203     TYPE_INTEGER,
11204     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11205   },
11206   {
11207     TYPE_INTEGER,
11208     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11209   },
11210   {
11211     TYPE_INTEGER,
11212     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11213   },
11214   {
11215     TYPE_INTEGER,
11216     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11217   },
11218   {
11219     TYPE_INTEGER,
11220     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11221   },
11222   {
11223     TYPE_INTEGER,
11224     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11225   },
11226   {
11227     TYPE_INTEGER,
11228     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11229   },
11230   {
11231     TYPE_INTEGER,
11232     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11233   },
11234   {
11235     TYPE_KEY_X11,
11236     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11237   },
11238   {
11239     TYPE_KEY_X11,
11240     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11241   },
11242   {
11243     TYPE_KEY_X11,
11244     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11245   },
11246   {
11247     TYPE_KEY_X11,
11248     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11249   },
11250   {
11251     TYPE_KEY_X11,
11252     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11253   },
11254   {
11255     TYPE_KEY_X11,
11256     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11257   },
11258   {
11259     TYPE_KEY_X11,
11260     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11261   },
11262   {
11263     TYPE_KEY_X11,
11264     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11265   },
11266   {
11267     TYPE_KEY_X11,
11268     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11269   },
11270   {
11271     TYPE_KEY_X11,
11272     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11273   },
11274   {
11275     TYPE_BOOLEAN,
11276     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11277   {
11278     TYPE_BOOLEAN,
11279     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11280   },
11281   {
11282     TYPE_BOOLEAN,
11283     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11284   },
11285   {
11286     TYPE_SWITCH3,
11287     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11288   },
11289   {
11290     TYPE_INTEGER,
11291     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11292   },
11293 };
11294
11295 static struct TokenInfo options_setup_tokens[] =
11296 {
11297   {
11298     TYPE_BOOLEAN,
11299     &setup.options.verbose,                     "options.verbose"
11300   },
11301   {
11302     TYPE_BOOLEAN,
11303     &setup.options.debug,                       "options.debug"
11304   },
11305   {
11306     TYPE_STRING,
11307     &setup.options.debug_mode,                  "options.debug_mode"
11308   },
11309 };
11310
11311 static void setSetupInfoToDefaults(struct SetupInfo *si)
11312 {
11313   int i;
11314
11315   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11316
11317   si->multiple_users = TRUE;
11318
11319   si->sound = TRUE;
11320   si->sound_loops = TRUE;
11321   si->sound_music = TRUE;
11322   si->sound_simple = TRUE;
11323   si->toons = TRUE;
11324   si->global_animations = TRUE;
11325   si->scroll_delay = TRUE;
11326   si->forced_scroll_delay = FALSE;
11327   si->scroll_delay_value = STD_SCROLL_DELAY;
11328   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11329   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11330   si->fade_screens = TRUE;
11331   si->autorecord = TRUE;
11332   si->autorecord_after_replay = TRUE;
11333   si->auto_pause_on_start = FALSE;
11334   si->show_titlescreen = TRUE;
11335   si->quick_doors = FALSE;
11336   si->team_mode = FALSE;
11337   si->handicap = TRUE;
11338   si->skip_levels = TRUE;
11339   si->increment_levels = TRUE;
11340   si->auto_play_next_level = TRUE;
11341   si->count_score_after_game = TRUE;
11342   si->show_scores_after_game = TRUE;
11343   si->time_limit = TRUE;
11344   si->fullscreen = FALSE;
11345   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11346   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11347   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11348   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11349   si->ask_on_escape = TRUE;
11350   si->ask_on_escape_editor = TRUE;
11351   si->ask_on_game_over = TRUE;
11352   si->ask_on_quit_game = TRUE;
11353   si->ask_on_quit_program = TRUE;
11354   si->quick_switch = FALSE;
11355   si->input_on_focus = FALSE;
11356   si->prefer_aga_graphics = TRUE;
11357   si->prefer_lowpass_sounds = FALSE;
11358   si->prefer_extra_panel_items = TRUE;
11359   si->game_speed_extended = FALSE;
11360   si->game_frame_delay = GAME_FRAME_DELAY;
11361   si->bd_skip_uncovering = FALSE;
11362   si->bd_skip_hatching = FALSE;
11363   si->bd_scroll_delay = TRUE;
11364   si->bd_smooth_movements = AUTO;
11365   si->sp_show_border_elements = FALSE;
11366   si->small_game_graphics = FALSE;
11367   si->show_load_save_buttons = FALSE;
11368   si->show_undo_redo_buttons = FALSE;
11369   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11370
11371   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11372   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11373   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11374
11375   si->override_level_graphics = FALSE;
11376   si->override_level_sounds = FALSE;
11377   si->override_level_music = FALSE;
11378
11379   si->volume_simple = 100;              // percent
11380   si->volume_loops = 100;               // percent
11381   si->volume_music = 100;               // percent
11382
11383   si->network_mode = FALSE;
11384   si->network_player_nr = 0;            // first player
11385   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11386
11387   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11388   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11389   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11390   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11391   si->touch.draw_outlined = TRUE;
11392   si->touch.draw_pressed = TRUE;
11393
11394   for (i = 0; i < 2; i++)
11395   {
11396     char *default_grid_button[6][2] =
11397     {
11398       { "      ", "  ^^  " },
11399       { "      ", "  ^^  " },
11400       { "      ", "<<  >>" },
11401       { "      ", "<<  >>" },
11402       { "111222", "  vv  " },
11403       { "111222", "  vv  " }
11404     };
11405     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11406     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11407     int min_xsize = MIN(6, grid_xsize);
11408     int min_ysize = MIN(6, grid_ysize);
11409     int startx = grid_xsize - min_xsize;
11410     int starty = grid_ysize - min_ysize;
11411     int x, y;
11412
11413     // virtual buttons grid can only be set to defaults if video is initialized
11414     // (this will be repeated if virtual buttons are not loaded from setup file)
11415     if (video.initialized)
11416     {
11417       si->touch.grid_xsize[i] = grid_xsize;
11418       si->touch.grid_ysize[i] = grid_ysize;
11419     }
11420     else
11421     {
11422       si->touch.grid_xsize[i] = -1;
11423       si->touch.grid_ysize[i] = -1;
11424     }
11425
11426     for (x = 0; x < MAX_GRID_XSIZE; x++)
11427       for (y = 0; y < MAX_GRID_YSIZE; y++)
11428         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11429
11430     for (x = 0; x < min_xsize; x++)
11431       for (y = 0; y < min_ysize; y++)
11432         si->touch.grid_button[i][x][starty + y] =
11433           default_grid_button[y][0][x];
11434
11435     for (x = 0; x < min_xsize; x++)
11436       for (y = 0; y < min_ysize; y++)
11437         si->touch.grid_button[i][startx + x][starty + y] =
11438           default_grid_button[y][1][x];
11439   }
11440
11441   si->touch.grid_initialized            = video.initialized;
11442
11443   si->touch.overlay_buttons             = FALSE;
11444
11445   si->editor.el_boulderdash             = TRUE;
11446   si->editor.el_boulderdash_native      = TRUE;
11447   si->editor.el_boulderdash_effects     = TRUE;
11448   si->editor.el_emerald_mine            = TRUE;
11449   si->editor.el_emerald_mine_club       = TRUE;
11450   si->editor.el_more                    = TRUE;
11451   si->editor.el_sokoban                 = TRUE;
11452   si->editor.el_supaplex                = TRUE;
11453   si->editor.el_diamond_caves           = TRUE;
11454   si->editor.el_dx_boulderdash          = TRUE;
11455
11456   si->editor.el_mirror_magic            = TRUE;
11457   si->editor.el_deflektor               = TRUE;
11458
11459   si->editor.el_chars                   = TRUE;
11460   si->editor.el_steel_chars             = TRUE;
11461
11462   si->editor.el_classic                 = TRUE;
11463   si->editor.el_custom                  = TRUE;
11464
11465   si->editor.el_user_defined            = FALSE;
11466   si->editor.el_dynamic                 = TRUE;
11467
11468   si->editor.el_headlines               = TRUE;
11469
11470   si->editor.show_element_token         = FALSE;
11471
11472   si->editor.show_read_only_warning     = TRUE;
11473
11474   si->editor.use_template_for_new_levels = TRUE;
11475
11476   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11477   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11478   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11479   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11480   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11481
11482   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11483   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11484   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11485   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11486   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11487
11488   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11489   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11490   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11491   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11492   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11493   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11494
11495   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11496   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11497   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11498
11499   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11500   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11501   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11502   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11503
11504   for (i = 0; i < MAX_PLAYERS; i++)
11505   {
11506     si->input[i].use_joystick = FALSE;
11507     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11508     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11509     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11510     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11511     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11512     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11513     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11514     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11515     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11516     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11517     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11518     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11519     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11520     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11521     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11522   }
11523
11524   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11525   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11526   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11527   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11528
11529   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11530   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11531   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11532   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11533   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11534   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11535   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11536
11537   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11538
11539   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11540   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11541   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11542
11543   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11544   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11545   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11546
11547   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11548   si->internal.choose_from_top_leveldir = FALSE;
11549   si->internal.show_scaling_in_title = TRUE;
11550   si->internal.create_user_levelset = TRUE;
11551   si->internal.info_screens_from_main = FALSE;
11552
11553   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11554   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11555
11556   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11557   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11558   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11559   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11560   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11561   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11562   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11563   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11564   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11565   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11566
11567   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11568   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11569   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11570   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11571   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11572   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11573   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11574   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11575   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11576   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11577
11578   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11579   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11580
11581   si->debug.show_frames_per_second = FALSE;
11582
11583   si->debug.xsn_mode = AUTO;
11584   si->debug.xsn_percent = 0;
11585
11586   si->options.verbose = FALSE;
11587   si->options.debug = FALSE;
11588   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11589
11590 #if defined(PLATFORM_ANDROID)
11591   si->fullscreen = TRUE;
11592   si->touch.overlay_buttons = TRUE;
11593 #endif
11594
11595   setHideSetupEntry(&setup.debug.xsn_mode);
11596 }
11597
11598 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11599 {
11600   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11601 }
11602
11603 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11604 {
11605   si->player_uuid = NULL;       // (will be set later)
11606   si->player_version = 1;       // (will be set later)
11607
11608   si->use_api_server = TRUE;
11609   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11610   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11611   si->ask_for_uploading_tapes = TRUE;
11612   si->ask_for_remaining_tapes = FALSE;
11613   si->provide_uploading_tapes = TRUE;
11614   si->ask_for_using_api_server = TRUE;
11615   si->has_remaining_tapes = FALSE;
11616 }
11617
11618 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11619 {
11620   si->editor_cascade.el_bd              = TRUE;
11621   si->editor_cascade.el_bd_native       = TRUE;
11622   si->editor_cascade.el_bd_effects      = FALSE;
11623   si->editor_cascade.el_em              = TRUE;
11624   si->editor_cascade.el_emc             = TRUE;
11625   si->editor_cascade.el_rnd             = TRUE;
11626   si->editor_cascade.el_sb              = TRUE;
11627   si->editor_cascade.el_sp              = TRUE;
11628   si->editor_cascade.el_dc              = TRUE;
11629   si->editor_cascade.el_dx              = TRUE;
11630
11631   si->editor_cascade.el_mm              = TRUE;
11632   si->editor_cascade.el_df              = TRUE;
11633
11634   si->editor_cascade.el_chars           = FALSE;
11635   si->editor_cascade.el_steel_chars     = FALSE;
11636   si->editor_cascade.el_ce              = FALSE;
11637   si->editor_cascade.el_ge              = FALSE;
11638   si->editor_cascade.el_es              = FALSE;
11639   si->editor_cascade.el_ref             = FALSE;
11640   si->editor_cascade.el_user            = FALSE;
11641   si->editor_cascade.el_dynamic         = FALSE;
11642 }
11643
11644 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11645
11646 static char *getHideSetupToken(void *setup_value)
11647 {
11648   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11649
11650   if (setup_value != NULL)
11651     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11652
11653   return hide_setup_token;
11654 }
11655
11656 void setHideSetupEntry(void *setup_value)
11657 {
11658   char *hide_setup_token = getHideSetupToken(setup_value);
11659
11660   if (hide_setup_hash == NULL)
11661     hide_setup_hash = newSetupFileHash();
11662
11663   if (setup_value != NULL)
11664     setHashEntry(hide_setup_hash, hide_setup_token, "");
11665 }
11666
11667 void removeHideSetupEntry(void *setup_value)
11668 {
11669   char *hide_setup_token = getHideSetupToken(setup_value);
11670
11671   if (setup_value != NULL)
11672     removeHashEntry(hide_setup_hash, hide_setup_token);
11673 }
11674
11675 boolean hideSetupEntry(void *setup_value)
11676 {
11677   char *hide_setup_token = getHideSetupToken(setup_value);
11678
11679   return (setup_value != NULL &&
11680           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11681 }
11682
11683 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11684                                       struct TokenInfo *token_info,
11685                                       int token_nr, char *token_text)
11686 {
11687   char *token_hide_text = getStringCat2(token_text, ".hide");
11688   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11689
11690   // set the value of this setup option in the setup option structure
11691   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11692
11693   // check if this setup option should be hidden in the setup menu
11694   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11695     setHideSetupEntry(token_info[token_nr].value);
11696
11697   free(token_hide_text);
11698 }
11699
11700 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11701                                       struct TokenInfo *token_info,
11702                                       int token_nr)
11703 {
11704   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11705                             token_info[token_nr].text);
11706 }
11707
11708 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11709 {
11710   int i, pnr;
11711
11712   if (!setup_file_hash)
11713     return;
11714
11715   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11716     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11717
11718   setup.touch.grid_initialized = TRUE;
11719   for (i = 0; i < 2; i++)
11720   {
11721     int grid_xsize = setup.touch.grid_xsize[i];
11722     int grid_ysize = setup.touch.grid_ysize[i];
11723     int x, y;
11724
11725     // if virtual buttons are not loaded from setup file, repeat initializing
11726     // virtual buttons grid with default values later when video is initialized
11727     if (grid_xsize == -1 ||
11728         grid_ysize == -1)
11729     {
11730       setup.touch.grid_initialized = FALSE;
11731
11732       continue;
11733     }
11734
11735     for (y = 0; y < grid_ysize; y++)
11736     {
11737       char token_string[MAX_LINE_LEN];
11738
11739       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11740
11741       char *value_string = getHashEntry(setup_file_hash, token_string);
11742
11743       if (value_string == NULL)
11744         continue;
11745
11746       for (x = 0; x < grid_xsize; x++)
11747       {
11748         char c = value_string[x];
11749
11750         setup.touch.grid_button[i][x][y] =
11751           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11752       }
11753     }
11754   }
11755
11756   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11757     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11758
11759   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11760     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11761
11762   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11763   {
11764     char prefix[30];
11765
11766     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11767
11768     setup_input = setup.input[pnr];
11769     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11770     {
11771       char full_token[100];
11772
11773       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11774       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11775                                 full_token);
11776     }
11777     setup.input[pnr] = setup_input;
11778   }
11779
11780   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11781     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11782
11783   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11784     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11785
11786   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11787     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11788
11789   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11790     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11791
11792   setHideRelatedSetupEntries();
11793 }
11794
11795 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11796 {
11797   int i;
11798
11799   if (!setup_file_hash)
11800     return;
11801
11802   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11803     setSetupInfo(auto_setup_tokens, i,
11804                  getHashEntry(setup_file_hash,
11805                               auto_setup_tokens[i].text));
11806 }
11807
11808 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11809 {
11810   int i;
11811
11812   if (!setup_file_hash)
11813     return;
11814
11815   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11816     setSetupInfo(server_setup_tokens, i,
11817                  getHashEntry(setup_file_hash,
11818                               server_setup_tokens[i].text));
11819 }
11820
11821 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11822 {
11823   int i;
11824
11825   if (!setup_file_hash)
11826     return;
11827
11828   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11829     setSetupInfo(editor_cascade_setup_tokens, i,
11830                  getHashEntry(setup_file_hash,
11831                               editor_cascade_setup_tokens[i].text));
11832 }
11833
11834 void LoadUserNames(void)
11835 {
11836   int last_user_nr = user.nr;
11837   int i;
11838
11839   if (global.user_names != NULL)
11840   {
11841     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11842       checked_free(global.user_names[i]);
11843
11844     checked_free(global.user_names);
11845   }
11846
11847   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11848
11849   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11850   {
11851     user.nr = i;
11852
11853     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11854
11855     if (setup_file_hash)
11856     {
11857       char *player_name = getHashEntry(setup_file_hash, "player_name");
11858
11859       global.user_names[i] = getFixedUserName(player_name);
11860
11861       freeSetupFileHash(setup_file_hash);
11862     }
11863
11864     if (global.user_names[i] == NULL)
11865       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11866   }
11867
11868   user.nr = last_user_nr;
11869 }
11870
11871 void LoadSetupFromFilename(char *filename)
11872 {
11873   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11874
11875   if (setup_file_hash)
11876   {
11877     decodeSetupFileHash_Default(setup_file_hash);
11878
11879     freeSetupFileHash(setup_file_hash);
11880   }
11881   else
11882   {
11883     Debug("setup", "using default setup values");
11884   }
11885 }
11886
11887 static void LoadSetup_SpecialPostProcessing(void)
11888 {
11889   char *player_name_new;
11890
11891   // needed to work around problems with fixed length strings
11892   player_name_new = getFixedUserName(setup.player_name);
11893   free(setup.player_name);
11894   setup.player_name = player_name_new;
11895
11896   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11897   if (setup.scroll_delay == FALSE)
11898   {
11899     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11900     setup.scroll_delay = TRUE;                  // now always "on"
11901   }
11902
11903   // make sure that scroll delay value stays inside valid range
11904   setup.scroll_delay_value =
11905     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11906 }
11907
11908 void LoadSetup_Default(void)
11909 {
11910   char *filename;
11911
11912   // always start with reliable default values
11913   setSetupInfoToDefaults(&setup);
11914
11915   // try to load setup values from default setup file
11916   filename = getDefaultSetupFilename();
11917
11918   if (fileExists(filename))
11919     LoadSetupFromFilename(filename);
11920
11921   // try to load setup values from platform setup file
11922   filename = getPlatformSetupFilename();
11923
11924   if (fileExists(filename))
11925     LoadSetupFromFilename(filename);
11926
11927   // try to load setup values from user setup file
11928   filename = getSetupFilename();
11929
11930   LoadSetupFromFilename(filename);
11931
11932   LoadSetup_SpecialPostProcessing();
11933 }
11934
11935 void LoadSetup_AutoSetup(void)
11936 {
11937   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11938   SetupFileHash *setup_file_hash = NULL;
11939
11940   // always start with reliable default values
11941   setSetupInfoToDefaults_AutoSetup(&setup);
11942
11943   setup_file_hash = loadSetupFileHash(filename);
11944
11945   if (setup_file_hash)
11946   {
11947     decodeSetupFileHash_AutoSetup(setup_file_hash);
11948
11949     freeSetupFileHash(setup_file_hash);
11950   }
11951
11952   free(filename);
11953 }
11954
11955 void LoadSetup_ServerSetup(void)
11956 {
11957   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11958   SetupFileHash *setup_file_hash = NULL;
11959
11960   // always start with reliable default values
11961   setSetupInfoToDefaults_ServerSetup(&setup);
11962
11963   setup_file_hash = loadSetupFileHash(filename);
11964
11965   if (setup_file_hash)
11966   {
11967     decodeSetupFileHash_ServerSetup(setup_file_hash);
11968
11969     freeSetupFileHash(setup_file_hash);
11970   }
11971
11972   free(filename);
11973
11974   if (setup.player_uuid == NULL)
11975   {
11976     // player UUID does not yet exist in setup file
11977     setup.player_uuid = getStringCopy(getUUID());
11978     setup.player_version = 2;
11979
11980     SaveSetup_ServerSetup();
11981   }
11982 }
11983
11984 void LoadSetup_EditorCascade(void)
11985 {
11986   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11987   SetupFileHash *setup_file_hash = NULL;
11988
11989   // always start with reliable default values
11990   setSetupInfoToDefaults_EditorCascade(&setup);
11991
11992   setup_file_hash = loadSetupFileHash(filename);
11993
11994   if (setup_file_hash)
11995   {
11996     decodeSetupFileHash_EditorCascade(setup_file_hash);
11997
11998     freeSetupFileHash(setup_file_hash);
11999   }
12000
12001   free(filename);
12002 }
12003
12004 void LoadSetup(void)
12005 {
12006   LoadSetup_Default();
12007   LoadSetup_AutoSetup();
12008   LoadSetup_ServerSetup();
12009   LoadSetup_EditorCascade();
12010 }
12011
12012 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12013                                            char *mapping_line)
12014 {
12015   char mapping_guid[MAX_LINE_LEN];
12016   char *mapping_start, *mapping_end;
12017
12018   // get GUID from game controller mapping line: copy complete line
12019   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12020   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12021
12022   // get GUID from game controller mapping line: cut after GUID part
12023   mapping_start = strchr(mapping_guid, ',');
12024   if (mapping_start != NULL)
12025     *mapping_start = '\0';
12026
12027   // cut newline from game controller mapping line
12028   mapping_end = strchr(mapping_line, '\n');
12029   if (mapping_end != NULL)
12030     *mapping_end = '\0';
12031
12032   // add mapping entry to game controller mappings hash
12033   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12034 }
12035
12036 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12037                                                  char *filename)
12038 {
12039   FILE *file;
12040
12041   if (!(file = fopen(filename, MODE_READ)))
12042   {
12043     Warn("cannot read game controller mappings file '%s'", filename);
12044
12045     return;
12046   }
12047
12048   while (!feof(file))
12049   {
12050     char line[MAX_LINE_LEN];
12051
12052     if (!fgets(line, MAX_LINE_LEN, file))
12053       break;
12054
12055     addGameControllerMappingToHash(mappings_hash, line);
12056   }
12057
12058   fclose(file);
12059 }
12060
12061 void SaveSetup_Default(void)
12062 {
12063   char *filename = getSetupFilename();
12064   FILE *file;
12065   int i, pnr;
12066
12067   InitUserDataDirectory();
12068
12069   if (!(file = fopen(filename, MODE_WRITE)))
12070   {
12071     Warn("cannot write setup file '%s'", filename);
12072
12073     return;
12074   }
12075
12076   fprintFileHeader(file, SETUP_FILENAME);
12077
12078   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12079   {
12080     // just to make things nicer :)
12081     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12082         global_setup_tokens[i].value == &setup.sound                    ||
12083         global_setup_tokens[i].value == &setup.graphics_set             ||
12084         global_setup_tokens[i].value == &setup.volume_simple            ||
12085         global_setup_tokens[i].value == &setup.network_mode             ||
12086         global_setup_tokens[i].value == &setup.touch.control_type       ||
12087         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12088         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12089       fprintf(file, "\n");
12090
12091     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12092   }
12093
12094   for (i = 0; i < 2; i++)
12095   {
12096     int grid_xsize = setup.touch.grid_xsize[i];
12097     int grid_ysize = setup.touch.grid_ysize[i];
12098     int x, y;
12099
12100     fprintf(file, "\n");
12101
12102     for (y = 0; y < grid_ysize; y++)
12103     {
12104       char token_string[MAX_LINE_LEN];
12105       char value_string[MAX_LINE_LEN];
12106
12107       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12108
12109       for (x = 0; x < grid_xsize; x++)
12110       {
12111         char c = setup.touch.grid_button[i][x][y];
12112
12113         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12114       }
12115
12116       value_string[grid_xsize] = '\0';
12117
12118       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12119     }
12120   }
12121
12122   fprintf(file, "\n");
12123   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12124     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12125
12126   fprintf(file, "\n");
12127   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12128     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12129
12130   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12131   {
12132     char prefix[30];
12133
12134     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12135     fprintf(file, "\n");
12136
12137     setup_input = setup.input[pnr];
12138     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12139       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12140   }
12141
12142   fprintf(file, "\n");
12143   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12144     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12145
12146   // (internal setup values not saved to user setup file)
12147
12148   fprintf(file, "\n");
12149   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12150     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12151         setup.debug.xsn_mode != AUTO)
12152       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12153
12154   fprintf(file, "\n");
12155   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12156     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12157
12158   fclose(file);
12159
12160   SetFilePermissions(filename, PERMS_PRIVATE);
12161 }
12162
12163 void SaveSetup_AutoSetup(void)
12164 {
12165   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12166   FILE *file;
12167   int i;
12168
12169   InitUserDataDirectory();
12170
12171   if (!(file = fopen(filename, MODE_WRITE)))
12172   {
12173     Warn("cannot write auto setup file '%s'", filename);
12174
12175     free(filename);
12176
12177     return;
12178   }
12179
12180   fprintFileHeader(file, AUTOSETUP_FILENAME);
12181
12182   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12183     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12184
12185   fclose(file);
12186
12187   SetFilePermissions(filename, PERMS_PRIVATE);
12188
12189   free(filename);
12190 }
12191
12192 void SaveSetup_ServerSetup(void)
12193 {
12194   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12195   FILE *file;
12196   int i;
12197
12198   InitUserDataDirectory();
12199
12200   if (!(file = fopen(filename, MODE_WRITE)))
12201   {
12202     Warn("cannot write server setup file '%s'", filename);
12203
12204     free(filename);
12205
12206     return;
12207   }
12208
12209   fprintFileHeader(file, SERVERSETUP_FILENAME);
12210
12211   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12212   {
12213     // just to make things nicer :)
12214     if (server_setup_tokens[i].value == &setup.use_api_server)
12215       fprintf(file, "\n");
12216
12217     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12218   }
12219
12220   fclose(file);
12221
12222   SetFilePermissions(filename, PERMS_PRIVATE);
12223
12224   free(filename);
12225 }
12226
12227 void SaveSetup_EditorCascade(void)
12228 {
12229   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12230   FILE *file;
12231   int i;
12232
12233   InitUserDataDirectory();
12234
12235   if (!(file = fopen(filename, MODE_WRITE)))
12236   {
12237     Warn("cannot write editor cascade state file '%s'", filename);
12238
12239     free(filename);
12240
12241     return;
12242   }
12243
12244   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12245
12246   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12247     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12248
12249   fclose(file);
12250
12251   SetFilePermissions(filename, PERMS_PRIVATE);
12252
12253   free(filename);
12254 }
12255
12256 void SaveSetup(void)
12257 {
12258   SaveSetup_Default();
12259   SaveSetup_AutoSetup();
12260   SaveSetup_ServerSetup();
12261   SaveSetup_EditorCascade();
12262 }
12263
12264 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12265                                                   char *filename)
12266 {
12267   FILE *file;
12268
12269   if (!(file = fopen(filename, MODE_WRITE)))
12270   {
12271     Warn("cannot write game controller mappings file '%s'", filename);
12272
12273     return;
12274   }
12275
12276   BEGIN_HASH_ITERATION(mappings_hash, itr)
12277   {
12278     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12279   }
12280   END_HASH_ITERATION(mappings_hash, itr)
12281
12282   fclose(file);
12283 }
12284
12285 void SaveSetup_AddGameControllerMapping(char *mapping)
12286 {
12287   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12288   SetupFileHash *mappings_hash = newSetupFileHash();
12289
12290   InitUserDataDirectory();
12291
12292   // load existing personal game controller mappings
12293   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12294
12295   // add new mapping to personal game controller mappings
12296   addGameControllerMappingToHash(mappings_hash, mapping);
12297
12298   // save updated personal game controller mappings
12299   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12300
12301   freeSetupFileHash(mappings_hash);
12302   free(filename);
12303 }
12304
12305 void LoadCustomElementDescriptions(void)
12306 {
12307   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12308   SetupFileHash *setup_file_hash;
12309   int i;
12310
12311   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12312   {
12313     if (element_info[i].custom_description != NULL)
12314     {
12315       free(element_info[i].custom_description);
12316       element_info[i].custom_description = NULL;
12317     }
12318   }
12319
12320   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12321     return;
12322
12323   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12324   {
12325     char *token = getStringCat2(element_info[i].token_name, ".name");
12326     char *value = getHashEntry(setup_file_hash, token);
12327
12328     if (value != NULL)
12329       element_info[i].custom_description = getStringCopy(value);
12330
12331     free(token);
12332   }
12333
12334   freeSetupFileHash(setup_file_hash);
12335 }
12336
12337 static int getElementFromToken(char *token)
12338 {
12339   char *value = getHashEntry(element_token_hash, token);
12340
12341   if (value != NULL)
12342     return atoi(value);
12343
12344   Warn("unknown element token '%s'", token);
12345
12346   return EL_UNDEFINED;
12347 }
12348
12349 void FreeGlobalAnimEventInfo(void)
12350 {
12351   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12352
12353   if (gaei->event_list == NULL)
12354     return;
12355
12356   int i;
12357
12358   for (i = 0; i < gaei->num_event_lists; i++)
12359   {
12360     checked_free(gaei->event_list[i]->event_value);
12361     checked_free(gaei->event_list[i]);
12362   }
12363
12364   checked_free(gaei->event_list);
12365
12366   gaei->event_list = NULL;
12367   gaei->num_event_lists = 0;
12368 }
12369
12370 static int AddGlobalAnimEventList(void)
12371 {
12372   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12373   int list_pos = gaei->num_event_lists++;
12374
12375   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12376                                      sizeof(struct GlobalAnimEventListInfo *));
12377
12378   gaei->event_list[list_pos] =
12379     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12380
12381   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12382
12383   gaeli->event_value = NULL;
12384   gaeli->num_event_values = 0;
12385
12386   return list_pos;
12387 }
12388
12389 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12390 {
12391   // do not add empty global animation events
12392   if (event_value == ANIM_EVENT_NONE)
12393     return list_pos;
12394
12395   // if list position is undefined, create new list
12396   if (list_pos == ANIM_EVENT_UNDEFINED)
12397     list_pos = AddGlobalAnimEventList();
12398
12399   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12400   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12401   int value_pos = gaeli->num_event_values++;
12402
12403   gaeli->event_value = checked_realloc(gaeli->event_value,
12404                                        gaeli->num_event_values * sizeof(int *));
12405
12406   gaeli->event_value[value_pos] = event_value;
12407
12408   return list_pos;
12409 }
12410
12411 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12412 {
12413   if (list_pos == ANIM_EVENT_UNDEFINED)
12414     return ANIM_EVENT_NONE;
12415
12416   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12417   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12418
12419   return gaeli->event_value[value_pos];
12420 }
12421
12422 int GetGlobalAnimEventValueCount(int list_pos)
12423 {
12424   if (list_pos == ANIM_EVENT_UNDEFINED)
12425     return 0;
12426
12427   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12428   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12429
12430   return gaeli->num_event_values;
12431 }
12432
12433 // This function checks if a string <s> of the format "string1, string2, ..."
12434 // exactly contains a string <s_contained>.
12435
12436 static boolean string_has_parameter(char *s, char *s_contained)
12437 {
12438   char *substring;
12439
12440   if (s == NULL || s_contained == NULL)
12441     return FALSE;
12442
12443   if (strlen(s_contained) > strlen(s))
12444     return FALSE;
12445
12446   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12447   {
12448     char next_char = s[strlen(s_contained)];
12449
12450     // check if next character is delimiter or whitespace
12451     if (next_char == ',' || next_char == '\0' ||
12452         next_char == ' ' || next_char == '\t')
12453       return TRUE;
12454   }
12455
12456   // check if string contains another parameter string after a comma
12457   substring = strchr(s, ',');
12458   if (substring == NULL)        // string does not contain a comma
12459     return FALSE;
12460
12461   // advance string pointer to next character after the comma
12462   substring++;
12463
12464   // skip potential whitespaces after the comma
12465   while (*substring == ' ' || *substring == '\t')
12466     substring++;
12467
12468   return string_has_parameter(substring, s_contained);
12469 }
12470
12471 static int get_anim_parameter_value_ce(char *s)
12472 {
12473   char *s_ptr = s;
12474   char *pattern_1 = "ce_change:custom_";
12475   char *pattern_2 = ".page_";
12476   int pattern_1_len = strlen(pattern_1);
12477   char *matching_char = strstr(s_ptr, pattern_1);
12478   int result = ANIM_EVENT_NONE;
12479
12480   if (matching_char == NULL)
12481     return ANIM_EVENT_NONE;
12482
12483   result = ANIM_EVENT_CE_CHANGE;
12484
12485   s_ptr = matching_char + pattern_1_len;
12486
12487   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12488   if (*s_ptr >= '0' && *s_ptr <= '9')
12489   {
12490     int gic_ce_nr = (*s_ptr++ - '0');
12491
12492     if (*s_ptr >= '0' && *s_ptr <= '9')
12493     {
12494       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12495
12496       if (*s_ptr >= '0' && *s_ptr <= '9')
12497         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12498     }
12499
12500     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12501       return ANIM_EVENT_NONE;
12502
12503     // custom element stored as 0 to 255
12504     gic_ce_nr--;
12505
12506     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12507   }
12508   else
12509   {
12510     // invalid custom element number specified
12511
12512     return ANIM_EVENT_NONE;
12513   }
12514
12515   // check for change page number ("page_X" or "page_XX") (optional)
12516   if (strPrefix(s_ptr, pattern_2))
12517   {
12518     s_ptr += strlen(pattern_2);
12519
12520     if (*s_ptr >= '0' && *s_ptr <= '9')
12521     {
12522       int gic_page_nr = (*s_ptr++ - '0');
12523
12524       if (*s_ptr >= '0' && *s_ptr <= '9')
12525         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12526
12527       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12528         return ANIM_EVENT_NONE;
12529
12530       // change page stored as 1 to 32 (0 means "all change pages")
12531
12532       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12533     }
12534     else
12535     {
12536       // invalid animation part number specified
12537
12538       return ANIM_EVENT_NONE;
12539     }
12540   }
12541
12542   // discard result if next character is neither delimiter nor whitespace
12543   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12544         *s_ptr == ' ' || *s_ptr == '\t'))
12545     return ANIM_EVENT_NONE;
12546
12547   return result;
12548 }
12549
12550 static int get_anim_parameter_value(char *s)
12551 {
12552   int event_value[] =
12553   {
12554     ANIM_EVENT_CLICK,
12555     ANIM_EVENT_INIT,
12556     ANIM_EVENT_START,
12557     ANIM_EVENT_END,
12558     ANIM_EVENT_POST
12559   };
12560   char *pattern_1[] =
12561   {
12562     "click:anim_",
12563     "init:anim_",
12564     "start:anim_",
12565     "end:anim_",
12566     "post:anim_"
12567   };
12568   char *pattern_2 = ".part_";
12569   char *matching_char = NULL;
12570   char *s_ptr = s;
12571   int pattern_1_len = 0;
12572   int result = ANIM_EVENT_NONE;
12573   int i;
12574
12575   result = get_anim_parameter_value_ce(s);
12576
12577   if (result != ANIM_EVENT_NONE)
12578     return result;
12579
12580   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12581   {
12582     matching_char = strstr(s_ptr, pattern_1[i]);
12583     pattern_1_len = strlen(pattern_1[i]);
12584     result = event_value[i];
12585
12586     if (matching_char != NULL)
12587       break;
12588   }
12589
12590   if (matching_char == NULL)
12591     return ANIM_EVENT_NONE;
12592
12593   s_ptr = matching_char + pattern_1_len;
12594
12595   // check for main animation number ("anim_X" or "anim_XX")
12596   if (*s_ptr >= '0' && *s_ptr <= '9')
12597   {
12598     int gic_anim_nr = (*s_ptr++ - '0');
12599
12600     if (*s_ptr >= '0' && *s_ptr <= '9')
12601       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12602
12603     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12604       return ANIM_EVENT_NONE;
12605
12606     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12607   }
12608   else
12609   {
12610     // invalid main animation number specified
12611
12612     return ANIM_EVENT_NONE;
12613   }
12614
12615   // check for animation part number ("part_X" or "part_XX") (optional)
12616   if (strPrefix(s_ptr, pattern_2))
12617   {
12618     s_ptr += strlen(pattern_2);
12619
12620     if (*s_ptr >= '0' && *s_ptr <= '9')
12621     {
12622       int gic_part_nr = (*s_ptr++ - '0');
12623
12624       if (*s_ptr >= '0' && *s_ptr <= '9')
12625         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12626
12627       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12628         return ANIM_EVENT_NONE;
12629
12630       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12631     }
12632     else
12633     {
12634       // invalid animation part number specified
12635
12636       return ANIM_EVENT_NONE;
12637     }
12638   }
12639
12640   // discard result if next character is neither delimiter nor whitespace
12641   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12642         *s_ptr == ' ' || *s_ptr == '\t'))
12643     return ANIM_EVENT_NONE;
12644
12645   return result;
12646 }
12647
12648 static int get_anim_parameter_values(char *s)
12649 {
12650   int list_pos = ANIM_EVENT_UNDEFINED;
12651   int event_value = ANIM_EVENT_DEFAULT;
12652
12653   if (string_has_parameter(s, "any"))
12654     event_value |= ANIM_EVENT_ANY;
12655
12656   if (string_has_parameter(s, "click:self") ||
12657       string_has_parameter(s, "click") ||
12658       string_has_parameter(s, "self"))
12659     event_value |= ANIM_EVENT_SELF;
12660
12661   if (string_has_parameter(s, "unclick:any"))
12662     event_value |= ANIM_EVENT_UNCLICK_ANY;
12663
12664   // if animation event found, add it to global animation event list
12665   if (event_value != ANIM_EVENT_NONE)
12666     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12667
12668   while (s != NULL)
12669   {
12670     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12671     event_value = get_anim_parameter_value(s);
12672
12673     // if animation event found, add it to global animation event list
12674     if (event_value != ANIM_EVENT_NONE)
12675       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12676
12677     // continue with next part of the string, starting with next comma
12678     s = strchr(s + 1, ',');
12679   }
12680
12681   return list_pos;
12682 }
12683
12684 static int get_anim_action_parameter_value(char *token)
12685 {
12686   // check most common default case first to massively speed things up
12687   if (strEqual(token, ARG_UNDEFINED))
12688     return ANIM_EVENT_ACTION_NONE;
12689
12690   int result = getImageIDFromToken(token);
12691
12692   if (result == -1)
12693   {
12694     char *gfx_token = getStringCat2("gfx.", token);
12695
12696     result = getImageIDFromToken(gfx_token);
12697
12698     checked_free(gfx_token);
12699   }
12700
12701   if (result == -1)
12702   {
12703     Key key = getKeyFromX11KeyName(token);
12704
12705     if (key != KSYM_UNDEFINED)
12706       result = -(int)key;
12707   }
12708
12709   if (result == -1)
12710   {
12711     if (isURL(token))
12712     {
12713       result = get_hash_from_string(token);     // unsigned int => int
12714       result = ABS(result);                     // may be negative now
12715       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12716
12717       setHashEntry(anim_url_hash, int2str(result, 0), token);
12718     }
12719   }
12720
12721   if (result == -1)
12722     result = ANIM_EVENT_ACTION_NONE;
12723
12724   return result;
12725 }
12726
12727 int get_parameter_value(char *value_raw, char *suffix, int type)
12728 {
12729   char *value = getStringToLower(value_raw);
12730   int result = 0;       // probably a save default value
12731
12732   if (strEqual(suffix, ".direction"))
12733   {
12734     result = (strEqual(value, "left")  ? MV_LEFT :
12735               strEqual(value, "right") ? MV_RIGHT :
12736               strEqual(value, "up")    ? MV_UP :
12737               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12738   }
12739   else if (strEqual(suffix, ".position"))
12740   {
12741     result = (strEqual(value, "left")   ? POS_LEFT :
12742               strEqual(value, "right")  ? POS_RIGHT :
12743               strEqual(value, "top")    ? POS_TOP :
12744               strEqual(value, "upper")  ? POS_UPPER :
12745               strEqual(value, "middle") ? POS_MIDDLE :
12746               strEqual(value, "lower")  ? POS_LOWER :
12747               strEqual(value, "bottom") ? POS_BOTTOM :
12748               strEqual(value, "any")    ? POS_ANY :
12749               strEqual(value, "ce")     ? POS_CE :
12750               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12751               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12752   }
12753   else if (strEqual(suffix, ".align"))
12754   {
12755     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12756               strEqual(value, "right")  ? ALIGN_RIGHT :
12757               strEqual(value, "center") ? ALIGN_CENTER :
12758               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12759   }
12760   else if (strEqual(suffix, ".valign"))
12761   {
12762     result = (strEqual(value, "top")    ? VALIGN_TOP :
12763               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12764               strEqual(value, "middle") ? VALIGN_MIDDLE :
12765               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12766   }
12767   else if (strEqual(suffix, ".anim_mode"))
12768   {
12769     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12770               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12771               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12772               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12773               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12774               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12775               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12776               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12777               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12778               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12779               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12780               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12781               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12782               string_has_parameter(value, "all")        ? ANIM_ALL :
12783               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12784               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12785               ANIM_DEFAULT);
12786
12787     if (string_has_parameter(value, "once"))
12788       result |= ANIM_ONCE;
12789
12790     if (string_has_parameter(value, "reverse"))
12791       result |= ANIM_REVERSE;
12792
12793     if (string_has_parameter(value, "opaque_player"))
12794       result |= ANIM_OPAQUE_PLAYER;
12795
12796     if (string_has_parameter(value, "static_panel"))
12797       result |= ANIM_STATIC_PANEL;
12798   }
12799   else if (strEqual(suffix, ".init_event") ||
12800            strEqual(suffix, ".anim_event"))
12801   {
12802     result = get_anim_parameter_values(value);
12803   }
12804   else if (strEqual(suffix, ".init_delay_action") ||
12805            strEqual(suffix, ".anim_delay_action") ||
12806            strEqual(suffix, ".post_delay_action") ||
12807            strEqual(suffix, ".init_event_action") ||
12808            strEqual(suffix, ".anim_event_action"))
12809   {
12810     result = get_anim_action_parameter_value(value_raw);
12811   }
12812   else if (strEqual(suffix, ".class"))
12813   {
12814     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12815               get_hash_from_string(value));
12816   }
12817   else if (strEqual(suffix, ".style"))
12818   {
12819     result = STYLE_DEFAULT;
12820
12821     if (string_has_parameter(value, "accurate_borders"))
12822       result |= STYLE_ACCURATE_BORDERS;
12823
12824     if (string_has_parameter(value, "inner_corners"))
12825       result |= STYLE_INNER_CORNERS;
12826
12827     if (string_has_parameter(value, "reverse"))
12828       result |= STYLE_REVERSE;
12829
12830     if (string_has_parameter(value, "leftmost_position"))
12831       result |= STYLE_LEFTMOST_POSITION;
12832
12833     if (string_has_parameter(value, "block_clicks"))
12834       result |= STYLE_BLOCK;
12835
12836     if (string_has_parameter(value, "passthrough_clicks"))
12837       result |= STYLE_PASSTHROUGH;
12838
12839     if (string_has_parameter(value, "multiple_actions"))
12840       result |= STYLE_MULTIPLE_ACTIONS;
12841
12842     if (string_has_parameter(value, "consume_ce_event"))
12843       result |= STYLE_CONSUME_CE_EVENT;
12844   }
12845   else if (strEqual(suffix, ".fade_mode"))
12846   {
12847     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12848               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12849               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12850               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12851               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12852               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12853               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12854               FADE_MODE_DEFAULT);
12855   }
12856   else if (strEqual(suffix, ".auto_delay_unit"))
12857   {
12858     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12859               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12860               AUTO_DELAY_UNIT_DEFAULT);
12861   }
12862   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12863   {
12864     result = gfx.get_font_from_token_function(value);
12865   }
12866   else          // generic parameter of type integer or boolean
12867   {
12868     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12869               type == TYPE_INTEGER ? get_integer_from_string(value) :
12870               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12871               ARG_UNDEFINED_VALUE);
12872   }
12873
12874   free(value);
12875
12876   return result;
12877 }
12878
12879 static int get_token_parameter_value(char *token, char *value_raw)
12880 {
12881   char *suffix;
12882
12883   if (token == NULL || value_raw == NULL)
12884     return ARG_UNDEFINED_VALUE;
12885
12886   suffix = strrchr(token, '.');
12887   if (suffix == NULL)
12888     suffix = token;
12889
12890   if (strEqual(suffix, ".element"))
12891     return getElementFromToken(value_raw);
12892
12893   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12894   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12895 }
12896
12897 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12898                                      boolean ignore_defaults)
12899 {
12900   int i;
12901
12902   for (i = 0; image_config_vars[i].token != NULL; i++)
12903   {
12904     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12905
12906     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12907     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12908       continue;
12909
12910     if (value != NULL)
12911       *image_config_vars[i].value =
12912         get_token_parameter_value(image_config_vars[i].token, value);
12913   }
12914 }
12915
12916 void InitMenuDesignSettings_Static(void)
12917 {
12918   // always start with reliable default values from static default config
12919   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12920 }
12921
12922 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12923 {
12924   int i;
12925
12926   // the following initializes hierarchical values from static configuration
12927
12928   // special case: initialize "ARG_DEFAULT" values in static default config
12929   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12930   titlescreen_initial_first_default.fade_mode  =
12931     title_initial_first_default.fade_mode;
12932   titlescreen_initial_first_default.fade_delay =
12933     title_initial_first_default.fade_delay;
12934   titlescreen_initial_first_default.post_delay =
12935     title_initial_first_default.post_delay;
12936   titlescreen_initial_first_default.auto_delay =
12937     title_initial_first_default.auto_delay;
12938   titlescreen_initial_first_default.auto_delay_unit =
12939     title_initial_first_default.auto_delay_unit;
12940   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12941   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12942   titlescreen_first_default.post_delay = title_first_default.post_delay;
12943   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12944   titlescreen_first_default.auto_delay_unit =
12945     title_first_default.auto_delay_unit;
12946   titlemessage_initial_first_default.fade_mode  =
12947     title_initial_first_default.fade_mode;
12948   titlemessage_initial_first_default.fade_delay =
12949     title_initial_first_default.fade_delay;
12950   titlemessage_initial_first_default.post_delay =
12951     title_initial_first_default.post_delay;
12952   titlemessage_initial_first_default.auto_delay =
12953     title_initial_first_default.auto_delay;
12954   titlemessage_initial_first_default.auto_delay_unit =
12955     title_initial_first_default.auto_delay_unit;
12956   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12957   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12958   titlemessage_first_default.post_delay = title_first_default.post_delay;
12959   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12960   titlemessage_first_default.auto_delay_unit =
12961     title_first_default.auto_delay_unit;
12962
12963   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12964   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12965   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12966   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12967   titlescreen_initial_default.auto_delay_unit =
12968     title_initial_default.auto_delay_unit;
12969   titlescreen_default.fade_mode  = title_default.fade_mode;
12970   titlescreen_default.fade_delay = title_default.fade_delay;
12971   titlescreen_default.post_delay = title_default.post_delay;
12972   titlescreen_default.auto_delay = title_default.auto_delay;
12973   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12974   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12975   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12976   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12977   titlemessage_initial_default.auto_delay_unit =
12978     title_initial_default.auto_delay_unit;
12979   titlemessage_default.fade_mode  = title_default.fade_mode;
12980   titlemessage_default.fade_delay = title_default.fade_delay;
12981   titlemessage_default.post_delay = title_default.post_delay;
12982   titlemessage_default.auto_delay = title_default.auto_delay;
12983   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12984
12985   // special case: initialize "ARG_DEFAULT" values in static default config
12986   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12987   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12988   {
12989     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12990     titlescreen_first[i] = titlescreen_first_default;
12991     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12992     titlemessage_first[i] = titlemessage_first_default;
12993
12994     titlescreen_initial[i] = titlescreen_initial_default;
12995     titlescreen[i] = titlescreen_default;
12996     titlemessage_initial[i] = titlemessage_initial_default;
12997     titlemessage[i] = titlemessage_default;
12998   }
12999
13000   // special case: initialize "ARG_DEFAULT" values in static default config
13001   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13002   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13003   {
13004     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13005       continue;
13006
13007     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13008     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13009     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13010   }
13011
13012   // special case: initialize "ARG_DEFAULT" values in static default config
13013   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13014   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13015   {
13016     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13017     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13018     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13019
13020     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13021       continue;
13022
13023     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13024   }
13025 }
13026
13027 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13028 {
13029   static struct
13030   {
13031     struct XY *dst, *src;
13032   }
13033   game_buttons_xy[] =
13034   {
13035     { &game.button.save,        &game.button.stop       },
13036     { &game.button.pause2,      &game.button.pause      },
13037     { &game.button.load,        &game.button.play       },
13038     { &game.button.undo,        &game.button.stop       },
13039     { &game.button.redo,        &game.button.play       },
13040
13041     { NULL,                     NULL                    }
13042   };
13043   int i, j;
13044
13045   // special case: initialize later added SETUP list size from LEVELS value
13046   if (menu.list_size[GAME_MODE_SETUP] == -1)
13047     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13048
13049   // set default position for snapshot buttons to stop/pause/play buttons
13050   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13051     if ((*game_buttons_xy[i].dst).x == -1 &&
13052         (*game_buttons_xy[i].dst).y == -1)
13053       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13054
13055   // --------------------------------------------------------------------------
13056   // dynamic viewports (including playfield margins, borders and alignments)
13057   // --------------------------------------------------------------------------
13058
13059   // dynamic viewports currently only supported for landscape mode
13060   int display_width  = MAX(video.display_width, video.display_height);
13061   int display_height = MIN(video.display_width, video.display_height);
13062
13063   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13064   {
13065     struct RectWithBorder *vp_window    = &viewport.window[i];
13066     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13067     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13068     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13069     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13070     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13071     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13072     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13073
13074     // adjust window size if min/max width/height is specified
13075
13076     if (vp_window->min_width != -1)
13077     {
13078       int window_width = display_width;
13079
13080       // when using static window height, use aspect ratio of display
13081       if (vp_window->min_height == -1)
13082         window_width = vp_window->height * display_width / display_height;
13083
13084       vp_window->width = MAX(vp_window->min_width, window_width);
13085     }
13086
13087     if (vp_window->min_height != -1)
13088     {
13089       int window_height = display_height;
13090
13091       // when using static window width, use aspect ratio of display
13092       if (vp_window->min_width == -1)
13093         window_height = vp_window->width * display_height / display_width;
13094
13095       vp_window->height = MAX(vp_window->min_height, window_height);
13096     }
13097
13098     if (vp_window->max_width != -1)
13099       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13100
13101     if (vp_window->max_height != -1)
13102       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13103
13104     int playfield_width  = vp_window->width;
13105     int playfield_height = vp_window->height;
13106
13107     // adjust playfield size and position according to specified margins
13108
13109     playfield_width  -= vp_playfield->margin_left;
13110     playfield_width  -= vp_playfield->margin_right;
13111
13112     playfield_height -= vp_playfield->margin_top;
13113     playfield_height -= vp_playfield->margin_bottom;
13114
13115     // adjust playfield size if min/max width/height is specified
13116
13117     if (vp_playfield->min_width != -1)
13118       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13119
13120     if (vp_playfield->min_height != -1)
13121       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13122
13123     if (vp_playfield->max_width != -1)
13124       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13125
13126     if (vp_playfield->max_height != -1)
13127       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13128
13129     // adjust playfield position according to specified alignment
13130
13131     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13132       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13133     else if (vp_playfield->align == ALIGN_CENTER)
13134       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13135     else if (vp_playfield->align == ALIGN_RIGHT)
13136       vp_playfield->x += playfield_width - vp_playfield->width;
13137
13138     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13139       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13140     else if (vp_playfield->valign == VALIGN_MIDDLE)
13141       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13142     else if (vp_playfield->valign == VALIGN_BOTTOM)
13143       vp_playfield->y += playfield_height - vp_playfield->height;
13144
13145     vp_playfield->x += vp_playfield->margin_left;
13146     vp_playfield->y += vp_playfield->margin_top;
13147
13148     // adjust individual playfield borders if only default border is specified
13149
13150     if (vp_playfield->border_left == -1)
13151       vp_playfield->border_left = vp_playfield->border_size;
13152     if (vp_playfield->border_right == -1)
13153       vp_playfield->border_right = vp_playfield->border_size;
13154     if (vp_playfield->border_top == -1)
13155       vp_playfield->border_top = vp_playfield->border_size;
13156     if (vp_playfield->border_bottom == -1)
13157       vp_playfield->border_bottom = vp_playfield->border_size;
13158
13159     // set dynamic playfield borders if borders are specified as undefined
13160     // (but only if window size was dynamic and playfield size was static)
13161
13162     if (dynamic_window_width && !dynamic_playfield_width)
13163     {
13164       if (vp_playfield->border_left == -1)
13165       {
13166         vp_playfield->border_left = (vp_playfield->x -
13167                                      vp_playfield->margin_left);
13168         vp_playfield->x     -= vp_playfield->border_left;
13169         vp_playfield->width += vp_playfield->border_left;
13170       }
13171
13172       if (vp_playfield->border_right == -1)
13173       {
13174         vp_playfield->border_right = (vp_window->width -
13175                                       vp_playfield->x -
13176                                       vp_playfield->width -
13177                                       vp_playfield->margin_right);
13178         vp_playfield->width += vp_playfield->border_right;
13179       }
13180     }
13181
13182     if (dynamic_window_height && !dynamic_playfield_height)
13183     {
13184       if (vp_playfield->border_top == -1)
13185       {
13186         vp_playfield->border_top = (vp_playfield->y -
13187                                     vp_playfield->margin_top);
13188         vp_playfield->y      -= vp_playfield->border_top;
13189         vp_playfield->height += vp_playfield->border_top;
13190       }
13191
13192       if (vp_playfield->border_bottom == -1)
13193       {
13194         vp_playfield->border_bottom = (vp_window->height -
13195                                        vp_playfield->y -
13196                                        vp_playfield->height -
13197                                        vp_playfield->margin_bottom);
13198         vp_playfield->height += vp_playfield->border_bottom;
13199       }
13200     }
13201
13202     // adjust playfield size to be a multiple of a defined alignment tile size
13203
13204     int align_size = vp_playfield->align_size;
13205     int playfield_xtiles = vp_playfield->width  / align_size;
13206     int playfield_ytiles = vp_playfield->height / align_size;
13207     int playfield_width_corrected  = playfield_xtiles * align_size;
13208     int playfield_height_corrected = playfield_ytiles * align_size;
13209     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13210                                  i == GFX_SPECIAL_ARG_EDITOR);
13211
13212     if (is_playfield_mode &&
13213         dynamic_playfield_width &&
13214         vp_playfield->width != playfield_width_corrected)
13215     {
13216       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13217
13218       vp_playfield->width = playfield_width_corrected;
13219
13220       if (vp_playfield->align == ALIGN_LEFT)
13221       {
13222         vp_playfield->border_left += playfield_xdiff;
13223       }
13224       else if (vp_playfield->align == ALIGN_RIGHT)
13225       {
13226         vp_playfield->border_right += playfield_xdiff;
13227       }
13228       else if (vp_playfield->align == ALIGN_CENTER)
13229       {
13230         int border_left_diff  = playfield_xdiff / 2;
13231         int border_right_diff = playfield_xdiff - border_left_diff;
13232
13233         vp_playfield->border_left  += border_left_diff;
13234         vp_playfield->border_right += border_right_diff;
13235       }
13236     }
13237
13238     if (is_playfield_mode &&
13239         dynamic_playfield_height &&
13240         vp_playfield->height != playfield_height_corrected)
13241     {
13242       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13243
13244       vp_playfield->height = playfield_height_corrected;
13245
13246       if (vp_playfield->valign == VALIGN_TOP)
13247       {
13248         vp_playfield->border_top += playfield_ydiff;
13249       }
13250       else if (vp_playfield->align == VALIGN_BOTTOM)
13251       {
13252         vp_playfield->border_right += playfield_ydiff;
13253       }
13254       else if (vp_playfield->align == VALIGN_MIDDLE)
13255       {
13256         int border_top_diff    = playfield_ydiff / 2;
13257         int border_bottom_diff = playfield_ydiff - border_top_diff;
13258
13259         vp_playfield->border_top    += border_top_diff;
13260         vp_playfield->border_bottom += border_bottom_diff;
13261       }
13262     }
13263
13264     // adjust door positions according to specified alignment
13265
13266     for (j = 0; j < 2; j++)
13267     {
13268       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13269
13270       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13271         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13272       else if (vp_door->align == ALIGN_CENTER)
13273         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13274       else if (vp_door->align == ALIGN_RIGHT)
13275         vp_door->x += vp_window->width - vp_door->width;
13276
13277       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13278         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13279       else if (vp_door->valign == VALIGN_MIDDLE)
13280         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13281       else if (vp_door->valign == VALIGN_BOTTOM)
13282         vp_door->y += vp_window->height - vp_door->height;
13283     }
13284   }
13285 }
13286
13287 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13288 {
13289   static struct
13290   {
13291     struct XYTileSize *dst, *src;
13292     int graphic;
13293   }
13294   editor_buttons_xy[] =
13295   {
13296     {
13297       &editor.button.element_left,      &editor.palette.element_left,
13298       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13299     },
13300     {
13301       &editor.button.element_middle,    &editor.palette.element_middle,
13302       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13303     },
13304     {
13305       &editor.button.element_right,     &editor.palette.element_right,
13306       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13307     },
13308
13309     { NULL,                     NULL                    }
13310   };
13311   int i;
13312
13313   // set default position for element buttons to element graphics
13314   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13315   {
13316     if ((*editor_buttons_xy[i].dst).x == -1 &&
13317         (*editor_buttons_xy[i].dst).y == -1)
13318     {
13319       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13320
13321       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13322
13323       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13324     }
13325   }
13326
13327   // adjust editor palette rows and columns if specified to be dynamic
13328
13329   if (editor.palette.cols == -1)
13330   {
13331     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13332     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13333     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13334
13335     editor.palette.cols = (vp_width - sc_width) / bt_width;
13336
13337     if (editor.palette.x == -1)
13338     {
13339       int palette_width = editor.palette.cols * bt_width + sc_width;
13340
13341       editor.palette.x = (vp_width - palette_width) / 2;
13342     }
13343   }
13344
13345   if (editor.palette.rows == -1)
13346   {
13347     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13348     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13349     int tx_height = getFontHeight(FONT_TEXT_2);
13350
13351     editor.palette.rows = (vp_height - tx_height) / bt_height;
13352
13353     if (editor.palette.y == -1)
13354     {
13355       int palette_height = editor.palette.rows * bt_height + tx_height;
13356
13357       editor.palette.y = (vp_height - palette_height) / 2;
13358     }
13359   }
13360 }
13361
13362 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13363                                                       boolean initialize)
13364 {
13365   // special case: check if network and preview player positions are redefined,
13366   // to compare this later against the main menu level preview being redefined
13367   struct TokenIntPtrInfo menu_config_players[] =
13368   {
13369     { "main.network_players.x", &menu.main.network_players.redefined    },
13370     { "main.network_players.y", &menu.main.network_players.redefined    },
13371     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13372     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13373     { "preview.x",              &preview.redefined                      },
13374     { "preview.y",              &preview.redefined                      }
13375   };
13376   int i;
13377
13378   if (initialize)
13379   {
13380     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13381       *menu_config_players[i].value = FALSE;
13382   }
13383   else
13384   {
13385     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13386       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13387         *menu_config_players[i].value = TRUE;
13388   }
13389 }
13390
13391 static void InitMenuDesignSettings_PreviewPlayers(void)
13392 {
13393   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13394 }
13395
13396 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13397 {
13398   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13399 }
13400
13401 static void LoadMenuDesignSettingsFromFilename(char *filename)
13402 {
13403   static struct TitleFadingInfo tfi;
13404   static struct TitleMessageInfo tmi;
13405   static struct TokenInfo title_tokens[] =
13406   {
13407     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13408     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13409     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13410     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13411     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13412
13413     { -1,               NULL,                   NULL                    }
13414   };
13415   static struct TokenInfo titlemessage_tokens[] =
13416   {
13417     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13418     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13419     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13420     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13421     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13422     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13423     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13424     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13425     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13426     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13427     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13428     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13429     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13430     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13431     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13432     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13433     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13434     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13435
13436     { -1,               NULL,                   NULL                    }
13437   };
13438   static struct
13439   {
13440     struct TitleFadingInfo *info;
13441     char *text;
13442   }
13443   title_info[] =
13444   {
13445     // initialize first titles from "enter screen" definitions, if defined
13446     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13447     { &title_first_default,             "menu.enter_screen.TITLE"       },
13448
13449     // initialize title screens from "next screen" definitions, if defined
13450     { &title_initial_default,           "menu.next_screen.TITLE"        },
13451     { &title_default,                   "menu.next_screen.TITLE"        },
13452
13453     { NULL,                             NULL                            }
13454   };
13455   static struct
13456   {
13457     struct TitleMessageInfo *array;
13458     char *text;
13459   }
13460   titlemessage_arrays[] =
13461   {
13462     // initialize first titles from "enter screen" definitions, if defined
13463     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13464     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13465     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13466     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13467
13468     // initialize titles from "next screen" definitions, if defined
13469     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13470     { titlescreen,                      "menu.next_screen.TITLE"        },
13471     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13472     { titlemessage,                     "menu.next_screen.TITLE"        },
13473
13474     // overwrite titles with title definitions, if defined
13475     { titlescreen_initial_first,        "[title_initial]"               },
13476     { titlescreen_first,                "[title]"                       },
13477     { titlemessage_initial_first,       "[title_initial]"               },
13478     { titlemessage_first,               "[title]"                       },
13479
13480     { titlescreen_initial,              "[title_initial]"               },
13481     { titlescreen,                      "[title]"                       },
13482     { titlemessage_initial,             "[title_initial]"               },
13483     { titlemessage,                     "[title]"                       },
13484
13485     // overwrite titles with title screen/message definitions, if defined
13486     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13487     { titlescreen_first,                "[titlescreen]"                 },
13488     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13489     { titlemessage_first,               "[titlemessage]"                },
13490
13491     { titlescreen_initial,              "[titlescreen_initial]"         },
13492     { titlescreen,                      "[titlescreen]"                 },
13493     { titlemessage_initial,             "[titlemessage_initial]"        },
13494     { titlemessage,                     "[titlemessage]"                },
13495
13496     { NULL,                             NULL                            }
13497   };
13498   SetupFileHash *setup_file_hash;
13499   int i, j, k;
13500
13501   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13502     return;
13503
13504   // the following initializes hierarchical values from dynamic configuration
13505
13506   // special case: initialize with default values that may be overwritten
13507   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13508   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13509   {
13510     struct TokenIntPtrInfo menu_config[] =
13511     {
13512       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13513       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13514       { "menu.list_size",       &menu.list_size[i]      }
13515     };
13516
13517     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13518     {
13519       char *token = menu_config[j].token;
13520       char *value = getHashEntry(setup_file_hash, token);
13521
13522       if (value != NULL)
13523         *menu_config[j].value = get_integer_from_string(value);
13524     }
13525   }
13526
13527   // special case: initialize with default values that may be overwritten
13528   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13529   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13530   {
13531     struct TokenIntPtrInfo menu_config[] =
13532     {
13533       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13534       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13535       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13536       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13537       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13538     };
13539
13540     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13541     {
13542       char *token = menu_config[j].token;
13543       char *value = getHashEntry(setup_file_hash, token);
13544
13545       if (value != NULL)
13546         *menu_config[j].value = get_integer_from_string(value);
13547     }
13548   }
13549
13550   // special case: initialize with default values that may be overwritten
13551   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13552   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13553   {
13554     struct TokenIntPtrInfo menu_config[] =
13555     {
13556       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13557       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13558     };
13559
13560     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13561     {
13562       char *token = menu_config[j].token;
13563       char *value = getHashEntry(setup_file_hash, token);
13564
13565       if (value != NULL)
13566         *menu_config[j].value = get_integer_from_string(value);
13567     }
13568   }
13569
13570   // special case: initialize with default values that may be overwritten
13571   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13572   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13573   {
13574     struct TokenIntPtrInfo menu_config[] =
13575     {
13576       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13577       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13578       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13579       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13580       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13581       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13582       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13583       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13584       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13585       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13586     };
13587
13588     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13589     {
13590       char *token = menu_config[j].token;
13591       char *value = getHashEntry(setup_file_hash, token);
13592
13593       if (value != NULL)
13594         *menu_config[j].value = get_integer_from_string(value);
13595     }
13596   }
13597
13598   // special case: initialize with default values that may be overwritten
13599   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13600   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13601   {
13602     struct TokenIntPtrInfo menu_config[] =
13603     {
13604       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13605       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13606       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13607       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13608       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13609       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13610       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13611       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13612       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13613     };
13614
13615     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13616     {
13617       char *token = menu_config[j].token;
13618       char *value = getHashEntry(setup_file_hash, token);
13619
13620       if (value != NULL)
13621         *menu_config[j].value = get_token_parameter_value(token, value);
13622     }
13623   }
13624
13625   // special case: initialize with default values that may be overwritten
13626   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13627   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13628   {
13629     struct
13630     {
13631       char *token_prefix;
13632       struct RectWithBorder *struct_ptr;
13633     }
13634     vp_struct[] =
13635     {
13636       { "viewport.window",      &viewport.window[i]     },
13637       { "viewport.playfield",   &viewport.playfield[i]  },
13638       { "viewport.door_1",      &viewport.door_1[i]     },
13639       { "viewport.door_2",      &viewport.door_2[i]     }
13640     };
13641
13642     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13643     {
13644       struct TokenIntPtrInfo vp_config[] =
13645       {
13646         { ".x",                 &vp_struct[j].struct_ptr->x             },
13647         { ".y",                 &vp_struct[j].struct_ptr->y             },
13648         { ".width",             &vp_struct[j].struct_ptr->width         },
13649         { ".height",            &vp_struct[j].struct_ptr->height        },
13650         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13651         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13652         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13653         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13654         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13655         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13656         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13657         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13658         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13659         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13660         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13661         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13662         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13663         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13664         { ".align",             &vp_struct[j].struct_ptr->align         },
13665         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13666       };
13667
13668       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13669       {
13670         char *token = getStringCat2(vp_struct[j].token_prefix,
13671                                     vp_config[k].token);
13672         char *value = getHashEntry(setup_file_hash, token);
13673
13674         if (value != NULL)
13675           *vp_config[k].value = get_token_parameter_value(token, value);
13676
13677         free(token);
13678       }
13679     }
13680   }
13681
13682   // special case: initialize with default values that may be overwritten
13683   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13684   for (i = 0; title_info[i].info != NULL; i++)
13685   {
13686     struct TitleFadingInfo *info = title_info[i].info;
13687     char *base_token = title_info[i].text;
13688
13689     for (j = 0; title_tokens[j].type != -1; j++)
13690     {
13691       char *token = getStringCat2(base_token, title_tokens[j].text);
13692       char *value = getHashEntry(setup_file_hash, token);
13693
13694       if (value != NULL)
13695       {
13696         int parameter_value = get_token_parameter_value(token, value);
13697
13698         tfi = *info;
13699
13700         *(int *)title_tokens[j].value = (int)parameter_value;
13701
13702         *info = tfi;
13703       }
13704
13705       free(token);
13706     }
13707   }
13708
13709   // special case: initialize with default values that may be overwritten
13710   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13711   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13712   {
13713     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13714     char *base_token = titlemessage_arrays[i].text;
13715
13716     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13717     {
13718       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13719       char *value = getHashEntry(setup_file_hash, token);
13720
13721       if (value != NULL)
13722       {
13723         int parameter_value = get_token_parameter_value(token, value);
13724
13725         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13726         {
13727           tmi = array[k];
13728
13729           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13730             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13731           else
13732             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13733
13734           array[k] = tmi;
13735         }
13736       }
13737
13738       free(token);
13739     }
13740   }
13741
13742   // read (and overwrite with) values that may be specified in config file
13743   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13744
13745   // special case: check if network and preview player positions are redefined
13746   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13747
13748   freeSetupFileHash(setup_file_hash);
13749 }
13750
13751 void LoadMenuDesignSettings(void)
13752 {
13753   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13754
13755   InitMenuDesignSettings_Static();
13756   InitMenuDesignSettings_SpecialPreProcessing();
13757   InitMenuDesignSettings_PreviewPlayers();
13758
13759   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13760   {
13761     // first look for special settings configured in level series config
13762     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13763
13764     if (fileExists(filename_base))
13765       LoadMenuDesignSettingsFromFilename(filename_base);
13766   }
13767
13768   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13769
13770   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13771     LoadMenuDesignSettingsFromFilename(filename_local);
13772
13773   InitMenuDesignSettings_SpecialPostProcessing();
13774 }
13775
13776 void LoadMenuDesignSettings_AfterGraphics(void)
13777 {
13778   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13779 }
13780
13781 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13782                                 boolean ignore_defaults)
13783 {
13784   int i;
13785
13786   for (i = 0; sound_config_vars[i].token != NULL; i++)
13787   {
13788     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13789
13790     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13791     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13792       continue;
13793
13794     if (value != NULL)
13795       *sound_config_vars[i].value =
13796         get_token_parameter_value(sound_config_vars[i].token, value);
13797   }
13798 }
13799
13800 void InitSoundSettings_Static(void)
13801 {
13802   // always start with reliable default values from static default config
13803   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13804 }
13805
13806 static void LoadSoundSettingsFromFilename(char *filename)
13807 {
13808   SetupFileHash *setup_file_hash;
13809
13810   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13811     return;
13812
13813   // read (and overwrite with) values that may be specified in config file
13814   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13815
13816   freeSetupFileHash(setup_file_hash);
13817 }
13818
13819 void LoadSoundSettings(void)
13820 {
13821   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13822
13823   InitSoundSettings_Static();
13824
13825   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13826   {
13827     // first look for special settings configured in level series config
13828     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13829
13830     if (fileExists(filename_base))
13831       LoadSoundSettingsFromFilename(filename_base);
13832   }
13833
13834   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13835
13836   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13837     LoadSoundSettingsFromFilename(filename_local);
13838 }
13839
13840 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13841 {
13842   char *filename = getEditorSetupFilename();
13843   SetupFileList *setup_file_list, *list;
13844   SetupFileHash *element_hash;
13845   int num_unknown_tokens = 0;
13846   int i;
13847
13848   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13849     return;
13850
13851   element_hash = newSetupFileHash();
13852
13853   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13854     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13855
13856   // determined size may be larger than needed (due to unknown elements)
13857   *num_elements = 0;
13858   for (list = setup_file_list; list != NULL; list = list->next)
13859     (*num_elements)++;
13860
13861   // add space for up to 3 more elements for padding that may be needed
13862   *num_elements += 3;
13863
13864   // free memory for old list of elements, if needed
13865   checked_free(*elements);
13866
13867   // allocate memory for new list of elements
13868   *elements = checked_malloc(*num_elements * sizeof(int));
13869
13870   *num_elements = 0;
13871   for (list = setup_file_list; list != NULL; list = list->next)
13872   {
13873     char *value = getHashEntry(element_hash, list->token);
13874
13875     if (value == NULL)          // try to find obsolete token mapping
13876     {
13877       char *mapped_token = get_mapped_token(list->token);
13878
13879       if (mapped_token != NULL)
13880       {
13881         value = getHashEntry(element_hash, mapped_token);
13882
13883         free(mapped_token);
13884       }
13885     }
13886
13887     if (value != NULL)
13888     {
13889       (*elements)[(*num_elements)++] = atoi(value);
13890     }
13891     else
13892     {
13893       if (num_unknown_tokens == 0)
13894       {
13895         Warn("---");
13896         Warn("unknown token(s) found in config file:");
13897         Warn("- config file: '%s'", filename);
13898
13899         num_unknown_tokens++;
13900       }
13901
13902       Warn("- token: '%s'", list->token);
13903     }
13904   }
13905
13906   if (num_unknown_tokens > 0)
13907     Warn("---");
13908
13909   while (*num_elements % 4)     // pad with empty elements, if needed
13910     (*elements)[(*num_elements)++] = EL_EMPTY;
13911
13912   freeSetupFileList(setup_file_list);
13913   freeSetupFileHash(element_hash);
13914
13915 #if 0
13916   for (i = 0; i < *num_elements; i++)
13917     Debug("editor", "element '%s' [%d]\n",
13918           element_info[(*elements)[i]].token_name, (*elements)[i]);
13919 #endif
13920 }
13921
13922 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13923                                                      boolean is_sound)
13924 {
13925   SetupFileHash *setup_file_hash = NULL;
13926   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13927   char *filename_music, *filename_prefix, *filename_info;
13928   struct
13929   {
13930     char *token;
13931     char **value_ptr;
13932   }
13933   token_to_value_ptr[] =
13934   {
13935     { "title_header",   &tmp_music_file_info.title_header       },
13936     { "artist_header",  &tmp_music_file_info.artist_header      },
13937     { "album_header",   &tmp_music_file_info.album_header       },
13938     { "year_header",    &tmp_music_file_info.year_header        },
13939     { "played_header",  &tmp_music_file_info.played_header      },
13940
13941     { "title",          &tmp_music_file_info.title              },
13942     { "artist",         &tmp_music_file_info.artist             },
13943     { "album",          &tmp_music_file_info.album              },
13944     { "year",           &tmp_music_file_info.year               },
13945     { "played",         &tmp_music_file_info.played             },
13946
13947     { NULL,             NULL                                    },
13948   };
13949   int i;
13950
13951   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13952                     getCustomMusicFilename(basename));
13953
13954   if (filename_music == NULL)
13955     return NULL;
13956
13957   // ---------- try to replace file extension ----------
13958
13959   filename_prefix = getStringCopy(filename_music);
13960   if (strrchr(filename_prefix, '.') != NULL)
13961     *strrchr(filename_prefix, '.') = '\0';
13962   filename_info = getStringCat2(filename_prefix, ".txt");
13963
13964   if (fileExists(filename_info))
13965     setup_file_hash = loadSetupFileHash(filename_info);
13966
13967   free(filename_prefix);
13968   free(filename_info);
13969
13970   if (setup_file_hash == NULL)
13971   {
13972     // ---------- try to add file extension ----------
13973
13974     filename_prefix = getStringCopy(filename_music);
13975     filename_info = getStringCat2(filename_prefix, ".txt");
13976
13977     if (fileExists(filename_info))
13978       setup_file_hash = loadSetupFileHash(filename_info);
13979
13980     free(filename_prefix);
13981     free(filename_info);
13982   }
13983
13984   if (setup_file_hash == NULL)
13985     return NULL;
13986
13987   // ---------- music file info found ----------
13988
13989   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13990
13991   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13992   {
13993     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13994
13995     *token_to_value_ptr[i].value_ptr =
13996       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13997   }
13998
13999   tmp_music_file_info.basename = getStringCopy(basename);
14000   tmp_music_file_info.music = music;
14001   tmp_music_file_info.is_sound = is_sound;
14002
14003   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14004   *new_music_file_info = tmp_music_file_info;
14005
14006   return new_music_file_info;
14007 }
14008
14009 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14010 {
14011   return get_music_file_info_ext(basename, music, FALSE);
14012 }
14013
14014 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14015 {
14016   return get_music_file_info_ext(basename, sound, TRUE);
14017 }
14018
14019 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14020                                      char *basename, boolean is_sound)
14021 {
14022   for (; list != NULL; list = list->next)
14023     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14024       return TRUE;
14025
14026   return FALSE;
14027 }
14028
14029 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14030 {
14031   return music_info_listed_ext(list, basename, FALSE);
14032 }
14033
14034 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14035 {
14036   return music_info_listed_ext(list, basename, TRUE);
14037 }
14038
14039 void LoadMusicInfo(void)
14040 {
14041   int num_music_noconf = getMusicListSize_NoConf();
14042   int num_music = getMusicListSize();
14043   int num_sounds = getSoundListSize();
14044   struct FileInfo *music, *sound;
14045   struct MusicFileInfo *next, **new;
14046
14047   int i;
14048
14049   while (music_file_info != NULL)
14050   {
14051     next = music_file_info->next;
14052
14053     checked_free(music_file_info->basename);
14054
14055     checked_free(music_file_info->title_header);
14056     checked_free(music_file_info->artist_header);
14057     checked_free(music_file_info->album_header);
14058     checked_free(music_file_info->year_header);
14059     checked_free(music_file_info->played_header);
14060
14061     checked_free(music_file_info->title);
14062     checked_free(music_file_info->artist);
14063     checked_free(music_file_info->album);
14064     checked_free(music_file_info->year);
14065     checked_free(music_file_info->played);
14066
14067     free(music_file_info);
14068
14069     music_file_info = next;
14070   }
14071
14072   new = &music_file_info;
14073
14074   // get (configured or unconfigured) music file info for all levels
14075   for (i = leveldir_current->first_level;
14076        i <= leveldir_current->last_level; i++)
14077   {
14078     int music_nr;
14079
14080     if (levelset.music[i] != MUS_UNDEFINED)
14081     {
14082       // get music file info for configured level music
14083       music_nr = levelset.music[i];
14084     }
14085     else if (num_music_noconf > 0)
14086     {
14087       // get music file info for unconfigured level music
14088       int level_pos = i - leveldir_current->first_level;
14089
14090       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14091     }
14092     else
14093     {
14094       continue;
14095     }
14096
14097     char *basename = getMusicInfoEntryFilename(music_nr);
14098
14099     if (basename == NULL)
14100       continue;
14101
14102     if (!music_info_listed(music_file_info, basename))
14103     {
14104       *new = get_music_file_info(basename, music_nr);
14105
14106       if (*new != NULL)
14107         new = &(*new)->next;
14108     }
14109   }
14110
14111   // get music file info for all remaining configured music files
14112   for (i = 0; i < num_music; i++)
14113   {
14114     music = getMusicListEntry(i);
14115
14116     if (music->filename == NULL)
14117       continue;
14118
14119     if (strEqual(music->filename, UNDEFINED_FILENAME))
14120       continue;
14121
14122     // a configured file may be not recognized as music
14123     if (!FileIsMusic(music->filename))
14124       continue;
14125
14126     if (!music_info_listed(music_file_info, music->filename))
14127     {
14128       *new = get_music_file_info(music->filename, i);
14129
14130       if (*new != NULL)
14131         new = &(*new)->next;
14132     }
14133   }
14134
14135   // get sound file info for all configured sound files
14136   for (i = 0; i < num_sounds; i++)
14137   {
14138     sound = getSoundListEntry(i);
14139
14140     if (sound->filename == NULL)
14141       continue;
14142
14143     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14144       continue;
14145
14146     // a configured file may be not recognized as sound
14147     if (!FileIsSound(sound->filename))
14148       continue;
14149
14150     if (!sound_info_listed(music_file_info, sound->filename))
14151     {
14152       *new = get_sound_file_info(sound->filename, i);
14153       if (*new != NULL)
14154         new = &(*new)->next;
14155     }
14156   }
14157
14158   // add pointers to previous list nodes
14159
14160   struct MusicFileInfo *node = music_file_info;
14161
14162   while (node != NULL)
14163   {
14164     if (node->next)
14165       node->next->prev = node;
14166
14167     node = node->next;
14168   }
14169 }
14170
14171 static void add_helpanim_entry(int element, int action, int direction,
14172                                int delay, int *num_list_entries)
14173 {
14174   struct HelpAnimInfo *new_list_entry;
14175   (*num_list_entries)++;
14176
14177   helpanim_info =
14178     checked_realloc(helpanim_info,
14179                     *num_list_entries * sizeof(struct HelpAnimInfo));
14180   new_list_entry = &helpanim_info[*num_list_entries - 1];
14181
14182   new_list_entry->element = element;
14183   new_list_entry->action = action;
14184   new_list_entry->direction = direction;
14185   new_list_entry->delay = delay;
14186 }
14187
14188 static void print_unknown_token(char *filename, char *token, int token_nr)
14189 {
14190   if (token_nr == 0)
14191   {
14192     Warn("---");
14193     Warn("unknown token(s) found in config file:");
14194     Warn("- config file: '%s'", filename);
14195   }
14196
14197   Warn("- token: '%s'", token);
14198 }
14199
14200 static void print_unknown_token_end(int token_nr)
14201 {
14202   if (token_nr > 0)
14203     Warn("---");
14204 }
14205
14206 void LoadHelpAnimInfo(void)
14207 {
14208   char *filename = getHelpAnimFilename();
14209   SetupFileList *setup_file_list = NULL, *list;
14210   SetupFileHash *element_hash, *action_hash, *direction_hash;
14211   int num_list_entries = 0;
14212   int num_unknown_tokens = 0;
14213   int i;
14214
14215   if (fileExists(filename))
14216     setup_file_list = loadSetupFileList(filename);
14217
14218   if (setup_file_list == NULL)
14219   {
14220     // use reliable default values from static configuration
14221     SetupFileList *insert_ptr;
14222
14223     insert_ptr = setup_file_list =
14224       newSetupFileList(helpanim_config[0].token,
14225                        helpanim_config[0].value);
14226
14227     for (i = 1; helpanim_config[i].token; i++)
14228       insert_ptr = addListEntry(insert_ptr,
14229                                 helpanim_config[i].token,
14230                                 helpanim_config[i].value);
14231   }
14232
14233   element_hash   = newSetupFileHash();
14234   action_hash    = newSetupFileHash();
14235   direction_hash = newSetupFileHash();
14236
14237   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14238     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14239
14240   for (i = 0; i < NUM_ACTIONS; i++)
14241     setHashEntry(action_hash, element_action_info[i].suffix,
14242                  i_to_a(element_action_info[i].value));
14243
14244   // do not store direction index (bit) here, but direction value!
14245   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14246     setHashEntry(direction_hash, element_direction_info[i].suffix,
14247                  i_to_a(1 << element_direction_info[i].value));
14248
14249   for (list = setup_file_list; list != NULL; list = list->next)
14250   {
14251     char *element_token, *action_token, *direction_token;
14252     char *element_value, *action_value, *direction_value;
14253     int delay = atoi(list->value);
14254
14255     if (strEqual(list->token, "end"))
14256     {
14257       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14258
14259       continue;
14260     }
14261
14262     /* first try to break element into element/action/direction parts;
14263        if this does not work, also accept combined "element[.act][.dir]"
14264        elements (like "dynamite.active"), which are unique elements */
14265
14266     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14267     {
14268       element_value = getHashEntry(element_hash, list->token);
14269       if (element_value != NULL)        // element found
14270         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14271                            &num_list_entries);
14272       else
14273       {
14274         // no further suffixes found -- this is not an element
14275         print_unknown_token(filename, list->token, num_unknown_tokens++);
14276       }
14277
14278       continue;
14279     }
14280
14281     // token has format "<prefix>.<something>"
14282
14283     action_token = strchr(list->token, '.');    // suffix may be action ...
14284     direction_token = action_token;             // ... or direction
14285
14286     element_token = getStringCopy(list->token);
14287     *strchr(element_token, '.') = '\0';
14288
14289     element_value = getHashEntry(element_hash, element_token);
14290
14291     if (element_value == NULL)          // this is no element
14292     {
14293       element_value = getHashEntry(element_hash, list->token);
14294       if (element_value != NULL)        // combined element found
14295         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14296                            &num_list_entries);
14297       else
14298         print_unknown_token(filename, list->token, num_unknown_tokens++);
14299
14300       free(element_token);
14301
14302       continue;
14303     }
14304
14305     action_value = getHashEntry(action_hash, action_token);
14306
14307     if (action_value != NULL)           // action found
14308     {
14309       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14310                     &num_list_entries);
14311
14312       free(element_token);
14313
14314       continue;
14315     }
14316
14317     direction_value = getHashEntry(direction_hash, direction_token);
14318
14319     if (direction_value != NULL)        // direction found
14320     {
14321       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14322                          &num_list_entries);
14323
14324       free(element_token);
14325
14326       continue;
14327     }
14328
14329     if (strchr(action_token + 1, '.') == NULL)
14330     {
14331       // no further suffixes found -- this is not an action nor direction
14332
14333       element_value = getHashEntry(element_hash, list->token);
14334       if (element_value != NULL)        // combined element found
14335         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14336                            &num_list_entries);
14337       else
14338         print_unknown_token(filename, list->token, num_unknown_tokens++);
14339
14340       free(element_token);
14341
14342       continue;
14343     }
14344
14345     // token has format "<prefix>.<suffix>.<something>"
14346
14347     direction_token = strchr(action_token + 1, '.');
14348
14349     action_token = getStringCopy(action_token);
14350     *strchr(action_token + 1, '.') = '\0';
14351
14352     action_value = getHashEntry(action_hash, action_token);
14353
14354     if (action_value == NULL)           // this is no action
14355     {
14356       element_value = getHashEntry(element_hash, list->token);
14357       if (element_value != NULL)        // combined element found
14358         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14359                            &num_list_entries);
14360       else
14361         print_unknown_token(filename, list->token, num_unknown_tokens++);
14362
14363       free(element_token);
14364       free(action_token);
14365
14366       continue;
14367     }
14368
14369     direction_value = getHashEntry(direction_hash, direction_token);
14370
14371     if (direction_value != NULL)        // direction found
14372     {
14373       add_helpanim_entry(atoi(element_value), atoi(action_value),
14374                          atoi(direction_value), delay, &num_list_entries);
14375
14376       free(element_token);
14377       free(action_token);
14378
14379       continue;
14380     }
14381
14382     // this is no direction
14383
14384     element_value = getHashEntry(element_hash, list->token);
14385     if (element_value != NULL)          // combined element found
14386       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14387                          &num_list_entries);
14388     else
14389       print_unknown_token(filename, list->token, num_unknown_tokens++);
14390
14391     free(element_token);
14392     free(action_token);
14393   }
14394
14395   print_unknown_token_end(num_unknown_tokens);
14396
14397   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14398   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14399
14400   freeSetupFileList(setup_file_list);
14401   freeSetupFileHash(element_hash);
14402   freeSetupFileHash(action_hash);
14403   freeSetupFileHash(direction_hash);
14404
14405 #if 0
14406   for (i = 0; i < num_list_entries; i++)
14407     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14408           EL_NAME(helpanim_info[i].element),
14409           helpanim_info[i].element,
14410           helpanim_info[i].action,
14411           helpanim_info[i].direction,
14412           helpanim_info[i].delay);
14413 #endif
14414 }
14415
14416 void LoadHelpTextInfo(void)
14417 {
14418   char *filename = getHelpTextFilename();
14419   int i;
14420
14421   if (helptext_info != NULL)
14422   {
14423     freeSetupFileHash(helptext_info);
14424     helptext_info = NULL;
14425   }
14426
14427   if (fileExists(filename))
14428     helptext_info = loadSetupFileHash(filename);
14429
14430   if (helptext_info == NULL)
14431   {
14432     // use reliable default values from static configuration
14433     helptext_info = newSetupFileHash();
14434
14435     for (i = 0; helptext_config[i].token; i++)
14436       setHashEntry(helptext_info,
14437                    helptext_config[i].token,
14438                    helptext_config[i].value);
14439   }
14440
14441 #if 0
14442   BEGIN_HASH_ITERATION(helptext_info, itr)
14443   {
14444     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14445           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14446   }
14447   END_HASH_ITERATION(hash, itr)
14448 #endif
14449 }
14450
14451
14452 // ----------------------------------------------------------------------------
14453 // convert levels
14454 // ----------------------------------------------------------------------------
14455
14456 #define MAX_NUM_CONVERT_LEVELS          1000
14457
14458 void ConvertLevels(void)
14459 {
14460   static LevelDirTree *convert_leveldir = NULL;
14461   static int convert_level_nr = -1;
14462   static int num_levels_handled = 0;
14463   static int num_levels_converted = 0;
14464   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14465   int i;
14466
14467   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14468                                                global.convert_leveldir);
14469
14470   if (convert_leveldir == NULL)
14471     Fail("no such level identifier: '%s'", global.convert_leveldir);
14472
14473   leveldir_current = convert_leveldir;
14474
14475   if (global.convert_level_nr != -1)
14476   {
14477     convert_leveldir->first_level = global.convert_level_nr;
14478     convert_leveldir->last_level  = global.convert_level_nr;
14479   }
14480
14481   convert_level_nr = convert_leveldir->first_level;
14482
14483   PrintLine("=", 79);
14484   Print("Converting levels\n");
14485   PrintLine("-", 79);
14486   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14487   Print("Level series name:       '%s'\n", convert_leveldir->name);
14488   Print("Level series author:     '%s'\n", convert_leveldir->author);
14489   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14490   PrintLine("=", 79);
14491   Print("\n");
14492
14493   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14494     levels_failed[i] = FALSE;
14495
14496   while (convert_level_nr <= convert_leveldir->last_level)
14497   {
14498     char *level_filename;
14499     boolean new_level;
14500
14501     level_nr = convert_level_nr++;
14502
14503     Print("Level %03d: ", level_nr);
14504
14505     LoadLevel(level_nr);
14506     if (level.no_level_file || level.no_valid_file)
14507     {
14508       Print("(no level)\n");
14509       continue;
14510     }
14511
14512     Print("converting level ... ");
14513
14514 #if 0
14515     // special case: conversion of some EMC levels as requested by ACME
14516     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14517 #endif
14518
14519     level_filename = getDefaultLevelFilename(level_nr);
14520     new_level = !fileExists(level_filename);
14521
14522     if (new_level)
14523     {
14524       SaveLevel(level_nr);
14525
14526       num_levels_converted++;
14527
14528       Print("converted.\n");
14529     }
14530     else
14531     {
14532       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14533         levels_failed[level_nr] = TRUE;
14534
14535       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14536     }
14537
14538     num_levels_handled++;
14539   }
14540
14541   Print("\n");
14542   PrintLine("=", 79);
14543   Print("Number of levels handled: %d\n", num_levels_handled);
14544   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14545          (num_levels_handled ?
14546           num_levels_converted * 100 / num_levels_handled : 0));
14547   PrintLine("-", 79);
14548   Print("Summary (for automatic parsing by scripts):\n");
14549   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14550          convert_leveldir->identifier, num_levels_converted,
14551          num_levels_handled,
14552          (num_levels_handled ?
14553           num_levels_converted * 100 / num_levels_handled : 0));
14554
14555   if (num_levels_handled != num_levels_converted)
14556   {
14557     Print(", FAILED:");
14558     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14559       if (levels_failed[i])
14560         Print(" %03d", i);
14561   }
14562
14563   Print("\n");
14564   PrintLine("=", 79);
14565
14566   CloseAllAndExit(0);
14567 }
14568
14569
14570 // ----------------------------------------------------------------------------
14571 // create and save images for use in level sketches (raw BMP format)
14572 // ----------------------------------------------------------------------------
14573
14574 void CreateLevelSketchImages(void)
14575 {
14576   Bitmap *bitmap1;
14577   Bitmap *bitmap2;
14578   int i;
14579
14580   InitElementPropertiesGfxElement();
14581
14582   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14583   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14584
14585   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14586   {
14587     int element = getMappedElement(i);
14588     char basename1[16];
14589     char basename2[16];
14590     char *filename1;
14591     char *filename2;
14592
14593     sprintf(basename1, "%04d.bmp", i);
14594     sprintf(basename2, "%04ds.bmp", i);
14595
14596     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14597     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14598
14599     DrawSizedElement(0, 0, element, TILESIZE);
14600     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14601
14602     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14603       Fail("cannot save level sketch image file '%s'", filename1);
14604
14605     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14606     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14607
14608     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14609       Fail("cannot save level sketch image file '%s'", filename2);
14610
14611     free(filename1);
14612     free(filename2);
14613
14614     // create corresponding SQL statements (for normal and small images)
14615     if (i < 1000)
14616     {
14617       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14618       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14619     }
14620
14621     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14622     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14623
14624     // optional: create content for forum level sketch demonstration post
14625     if (options.debug)
14626       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14627   }
14628
14629   FreeBitmap(bitmap1);
14630   FreeBitmap(bitmap2);
14631
14632   if (options.debug)
14633     fprintf(stderr, "\n");
14634
14635   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14636
14637   CloseAllAndExit(0);
14638 }
14639
14640
14641 // ----------------------------------------------------------------------------
14642 // create and save images for element collecting animations (raw BMP format)
14643 // ----------------------------------------------------------------------------
14644
14645 static boolean createCollectImage(int element)
14646 {
14647   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14648 }
14649
14650 void CreateCollectElementImages(void)
14651 {
14652   int i, j;
14653   int num_steps = 8;
14654   int anim_frames = num_steps - 1;
14655   int tile_size = TILESIZE;
14656   int anim_width  = tile_size * anim_frames;
14657   int anim_height = tile_size;
14658   int num_collect_images = 0;
14659   int pos_collect_images = 0;
14660
14661   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14662     if (createCollectImage(i))
14663       num_collect_images++;
14664
14665   Info("Creating %d element collecting animation images ...",
14666        num_collect_images);
14667
14668   int dst_width  = anim_width * 2;
14669   int dst_height = anim_height * num_collect_images / 2;
14670   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14671   char *basename_bmp = "RocksCollect.bmp";
14672   char *basename_png = "RocksCollect.png";
14673   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14674   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14675   int len_filename_bmp = strlen(filename_bmp);
14676   int len_filename_png = strlen(filename_png);
14677   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14678   char cmd_convert[max_command_len];
14679
14680   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14681            filename_bmp,
14682            filename_png);
14683
14684   // force using RGBA surface for destination bitmap
14685   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14686                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14687
14688   dst_bitmap->surface =
14689     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14690
14691   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14692   {
14693     if (!createCollectImage(i))
14694       continue;
14695
14696     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14697     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14698     int graphic = el2img(i);
14699     char *token_name = element_info[i].token_name;
14700     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14701     Bitmap *src_bitmap;
14702     int src_x, src_y;
14703
14704     Info("- creating collecting image for '%s' ...", token_name);
14705
14706     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14707
14708     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14709                tile_size, tile_size, 0, 0);
14710
14711     // force using RGBA surface for temporary bitmap (using transparent black)
14712     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14713                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14714
14715     tmp_bitmap->surface =
14716       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14717
14718     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14719
14720     for (j = 0; j < anim_frames; j++)
14721     {
14722       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14723       int frame_size = frame_size_final * num_steps;
14724       int offset = (tile_size - frame_size_final) / 2;
14725       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14726
14727       while (frame_size > frame_size_final)
14728       {
14729         frame_size /= 2;
14730
14731         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14732
14733         FreeBitmap(frame_bitmap);
14734
14735         frame_bitmap = half_bitmap;
14736       }
14737
14738       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14739                        frame_size_final, frame_size_final,
14740                        dst_x + j * tile_size + offset, dst_y + offset);
14741
14742       FreeBitmap(frame_bitmap);
14743     }
14744
14745     tmp_bitmap->surface_masked = NULL;
14746
14747     FreeBitmap(tmp_bitmap);
14748
14749     pos_collect_images++;
14750   }
14751
14752   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14753     Fail("cannot save element collecting image file '%s'", filename_bmp);
14754
14755   FreeBitmap(dst_bitmap);
14756
14757   Info("Converting image file from BMP to PNG ...");
14758
14759   if (system(cmd_convert) != 0)
14760     Fail("converting image file failed");
14761
14762   unlink(filename_bmp);
14763
14764   Info("Done.");
14765
14766   CloseAllAndExit(0);
14767 }
14768
14769
14770 // ----------------------------------------------------------------------------
14771 // create and save images for custom and group elements (raw BMP format)
14772 // ----------------------------------------------------------------------------
14773
14774 void CreateCustomElementImages(char *directory)
14775 {
14776   char *src_basename = "RocksCE-template.ilbm";
14777   char *dst_basename = "RocksCE.bmp";
14778   char *src_filename = getPath2(directory, src_basename);
14779   char *dst_filename = getPath2(directory, dst_basename);
14780   Bitmap *src_bitmap;
14781   Bitmap *bitmap;
14782   int yoffset_ce = 0;
14783   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14784   int i;
14785
14786   InitVideoDefaults();
14787
14788   ReCreateBitmap(&backbuffer, video.width, video.height);
14789
14790   src_bitmap = LoadImage(src_filename);
14791
14792   bitmap = CreateBitmap(TILEX * 16 * 2,
14793                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14794                         DEFAULT_DEPTH);
14795
14796   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14797   {
14798     int x = i % 16;
14799     int y = i / 16;
14800     int ii = i + 1;
14801     int j;
14802
14803     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14804                TILEX * x, TILEY * y + yoffset_ce);
14805
14806     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14807                TILEX, TILEY,
14808                TILEX * x + TILEX * 16,
14809                TILEY * y + yoffset_ce);
14810
14811     for (j = 2; j >= 0; j--)
14812     {
14813       int c = ii % 10;
14814
14815       BlitBitmap(src_bitmap, bitmap,
14816                  TILEX + c * 7, 0, 6, 10,
14817                  TILEX * x + 6 + j * 7,
14818                  TILEY * y + 11 + yoffset_ce);
14819
14820       BlitBitmap(src_bitmap, bitmap,
14821                  TILEX + c * 8, TILEY, 6, 10,
14822                  TILEX * 16 + TILEX * x + 6 + j * 8,
14823                  TILEY * y + 10 + yoffset_ce);
14824
14825       ii /= 10;
14826     }
14827   }
14828
14829   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14830   {
14831     int x = i % 16;
14832     int y = i / 16;
14833     int ii = i + 1;
14834     int j;
14835
14836     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14837                TILEX * x, TILEY * y + yoffset_ge);
14838
14839     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14840                TILEX, TILEY,
14841                TILEX * x + TILEX * 16,
14842                TILEY * y + yoffset_ge);
14843
14844     for (j = 1; j >= 0; j--)
14845     {
14846       int c = ii % 10;
14847
14848       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14849                  TILEX * x + 6 + j * 10,
14850                  TILEY * y + 11 + yoffset_ge);
14851
14852       BlitBitmap(src_bitmap, bitmap,
14853                  TILEX + c * 8, TILEY + 12, 6, 10,
14854                  TILEX * 16 + TILEX * x + 10 + j * 8,
14855                  TILEY * y + 10 + yoffset_ge);
14856
14857       ii /= 10;
14858     }
14859   }
14860
14861   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14862     Fail("cannot save CE graphics file '%s'", dst_filename);
14863
14864   FreeBitmap(bitmap);
14865
14866   CloseAllAndExit(0);
14867 }