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