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