fixed compiler warning
[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 "editor.h"
23 #include "tools.h"
24 #include "tape.h"
25 #include "config.h"
26 #include "api.h"
27
28 #define ENABLE_UNUSED_CODE      0       // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
30 #define ENABLE_RESERVED_CODE    0       // reserved for later use
31
32 #define CHUNK_ID_LEN            4       // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
35
36 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
38
39 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
50
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
53
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
58
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
61
62 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
182     &li.time,                           100
183   },
184   {
185     -1,                                 SAVE_CONF_ALWAYS,
186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
187     &li.gems_needed,                    0
188   },
189   {
190     -1,                                 -1,
191     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
192     &li.random_seed,                    0
193   },
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204   {
205     -1,                                 -1,
206     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
207     &li.em_slippery_gems,               FALSE
208   },
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214   {
215     -1,                                 -1,
216     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
217     &li.can_move_into_acid_bits,        ~0      // default: everything can
218   },
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      // default: always deadly
223   },
224   {
225     -1,                                 -1,
226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
227     &li.em_explodes_by_fire,            FALSE
228   },
229   {
230     -1,                                 -1,
231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
232     &li.score[SC_TIME_BONUS],           1
233   },
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
237     &li.auto_exit_sokoban,              FALSE
238   },
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
242     &li.auto_count_gems,                FALSE
243   },
244   {
245     -1,                                 -1,
246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
247     &li.solved_by_one_player,           FALSE
248   },
249   {
250     -1,                                 -1,
251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
252     &li.time_score_base,                1
253   },
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
257     &li.rate_time_over_score,           FALSE
258   },
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
262     &li.bd_intermission,                FALSE
263   },
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
267     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
268   },
269   {
270     -1,                                 -1,
271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
272     &li.bd_pal_timing,                  FALSE
273   },
274   {
275     -1,                                 -1,
276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
277     &li.bd_cycle_delay_ms,              160
278   },
279   {
280     -1,                                 -1,
281     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
282     &li.bd_cycle_delay_c64,             0
283   },
284   {
285     -1,                                 -1,
286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
287     &li.bd_hatching_delay_cycles,       21
288   },
289   {
290     -1,                                 -1,
291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
292     &li.bd_hatching_delay_seconds,      2
293   },
294   {
295     -1,                                 -1,
296     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
297     &li.bd_line_shifting_borders,       FALSE
298   },
299   {
300     -1,                                 -1,
301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
302     &li.bd_scan_first_and_last_row,     TRUE
303   },
304   {
305     -1,                                 -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
307     &li.bd_short_explosions,            TRUE
308   },
309   {
310     -1,                                 -1,
311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
312     &li.bd_cave_random_seed_c64,        0
313   },
314   {
315     -1,                                 -1,
316     TYPE_INTEGER,                       CONF_VALUE_32_BIT(3),
317     &li.bd_color_b,                     GD_C64_COLOR(0)
318   },
319   {
320     -1,                                 -1,
321     TYPE_INTEGER,                       CONF_VALUE_32_BIT(4),
322     &li.bd_color_0,                     GD_C64_COLOR(0)
323   },
324   {
325     -1,                                 -1,
326     TYPE_INTEGER,                       CONF_VALUE_32_BIT(5),
327     &li.bd_color_1,                     GD_C64_COLOR(8)
328   },
329   {
330     -1,                                 -1,
331     TYPE_INTEGER,                       CONF_VALUE_32_BIT(6),
332     &li.bd_color_2,                     GD_C64_COLOR(11)
333   },
334   {
335     -1,                                 -1,
336     TYPE_INTEGER,                       CONF_VALUE_32_BIT(7),
337     &li.bd_color_3,                     GD_C64_COLOR(1)
338   },
339   {
340     -1,                                 -1,
341     TYPE_INTEGER,                       CONF_VALUE_32_BIT(8),
342     &li.bd_color_4,                     GD_C64_COLOR(5)
343   },
344   {
345     -1,                                 -1,
346     TYPE_INTEGER,                       CONF_VALUE_32_BIT(9),
347     &li.bd_color_5,                     GD_C64_COLOR(6)
348   },
349
350   {
351     -1,                                 -1,
352     -1,                                 -1,
353     NULL,                               -1
354   }
355 };
356
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
358 {
359   // (these values are the same for each player)
360   {
361     EL_PLAYER_1,                        -1,
362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
363     &li.block_last_field,               FALSE   // default case for EM levels
364   },
365   {
366     EL_PLAYER_1,                        -1,
367     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
368     &li.sp_block_last_field,            TRUE    // default case for SP levels
369   },
370   {
371     EL_PLAYER_1,                        -1,
372     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
373     &li.instant_relocation,             FALSE
374   },
375   {
376     EL_PLAYER_1,                        -1,
377     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
378     &li.can_pass_to_walkable,           FALSE
379   },
380   {
381     EL_PLAYER_1,                        -1,
382     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
383     &li.block_snap_field,               TRUE
384   },
385   {
386     EL_PLAYER_1,                        -1,
387     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
388     &li.continuous_snapping,            TRUE
389   },
390   {
391     EL_PLAYER_1,                        -1,
392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
393     &li.shifted_relocation,             FALSE
394   },
395   {
396     EL_PLAYER_1,                        -1,
397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
398     &li.lazy_relocation,                FALSE
399   },
400   {
401     EL_PLAYER_1,                        -1,
402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
403     &li.finish_dig_collect,             TRUE
404   },
405   {
406     EL_PLAYER_1,                        -1,
407     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
408     &li.keep_walkable_ce,               FALSE
409   },
410
411   // (these values are different for each player)
412   {
413     EL_PLAYER_1,                        -1,
414     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
415     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
416   },
417   {
418     EL_PLAYER_1,                        -1,
419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
420     &li.initial_player_gravity[0],      FALSE
421   },
422   {
423     EL_PLAYER_1,                        -1,
424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
425     &li.use_start_element[0],           FALSE
426   },
427   {
428     EL_PLAYER_1,                        -1,
429     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
430     &li.start_element[0],               EL_PLAYER_1
431   },
432   {
433     EL_PLAYER_1,                        -1,
434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
435     &li.use_artwork_element[0],         FALSE
436   },
437   {
438     EL_PLAYER_1,                        -1,
439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
440     &li.artwork_element[0],             EL_PLAYER_1
441   },
442   {
443     EL_PLAYER_1,                        -1,
444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
445     &li.use_explosion_element[0],       FALSE
446   },
447   {
448     EL_PLAYER_1,                        -1,
449     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
450     &li.explosion_element[0],           EL_PLAYER_1
451   },
452   {
453     EL_PLAYER_1,                        -1,
454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
455     &li.use_initial_inventory[0],       FALSE
456   },
457   {
458     EL_PLAYER_1,                        -1,
459     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
460     &li.initial_inventory_size[0],      1
461   },
462   {
463     EL_PLAYER_1,                        -1,
464     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
465     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
467   },
468
469   {
470     EL_PLAYER_2,                        -1,
471     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
472     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
473   },
474   {
475     EL_PLAYER_2,                        -1,
476     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
477     &li.initial_player_gravity[1],      FALSE
478   },
479   {
480     EL_PLAYER_2,                        -1,
481     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
482     &li.use_start_element[1],           FALSE
483   },
484   {
485     EL_PLAYER_2,                        -1,
486     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
487     &li.start_element[1],               EL_PLAYER_2
488   },
489   {
490     EL_PLAYER_2,                        -1,
491     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
492     &li.use_artwork_element[1],         FALSE
493   },
494   {
495     EL_PLAYER_2,                        -1,
496     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
497     &li.artwork_element[1],             EL_PLAYER_2
498   },
499   {
500     EL_PLAYER_2,                        -1,
501     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
502     &li.use_explosion_element[1],       FALSE
503   },
504   {
505     EL_PLAYER_2,                        -1,
506     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
507     &li.explosion_element[1],           EL_PLAYER_2
508   },
509   {
510     EL_PLAYER_2,                        -1,
511     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
512     &li.use_initial_inventory[1],       FALSE
513   },
514   {
515     EL_PLAYER_2,                        -1,
516     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
517     &li.initial_inventory_size[1],      1
518   },
519   {
520     EL_PLAYER_2,                        -1,
521     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
522     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
524   },
525
526   {
527     EL_PLAYER_3,                        -1,
528     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
529     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
530   },
531   {
532     EL_PLAYER_3,                        -1,
533     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
534     &li.initial_player_gravity[2],      FALSE
535   },
536   {
537     EL_PLAYER_3,                        -1,
538     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
539     &li.use_start_element[2],           FALSE
540   },
541   {
542     EL_PLAYER_3,                        -1,
543     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
544     &li.start_element[2],               EL_PLAYER_3
545   },
546   {
547     EL_PLAYER_3,                        -1,
548     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
549     &li.use_artwork_element[2],         FALSE
550   },
551   {
552     EL_PLAYER_3,                        -1,
553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
554     &li.artwork_element[2],             EL_PLAYER_3
555   },
556   {
557     EL_PLAYER_3,                        -1,
558     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
559     &li.use_explosion_element[2],       FALSE
560   },
561   {
562     EL_PLAYER_3,                        -1,
563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
564     &li.explosion_element[2],           EL_PLAYER_3
565   },
566   {
567     EL_PLAYER_3,                        -1,
568     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
569     &li.use_initial_inventory[2],       FALSE
570   },
571   {
572     EL_PLAYER_3,                        -1,
573     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
574     &li.initial_inventory_size[2],      1
575   },
576   {
577     EL_PLAYER_3,                        -1,
578     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
579     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
581   },
582
583   {
584     EL_PLAYER_4,                        -1,
585     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
586     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
587   },
588   {
589     EL_PLAYER_4,                        -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
591     &li.initial_player_gravity[3],      FALSE
592   },
593   {
594     EL_PLAYER_4,                        -1,
595     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
596     &li.use_start_element[3],           FALSE
597   },
598   {
599     EL_PLAYER_4,                        -1,
600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
601     &li.start_element[3],               EL_PLAYER_4
602   },
603   {
604     EL_PLAYER_4,                        -1,
605     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
606     &li.use_artwork_element[3],         FALSE
607   },
608   {
609     EL_PLAYER_4,                        -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
611     &li.artwork_element[3],             EL_PLAYER_4
612   },
613   {
614     EL_PLAYER_4,                        -1,
615     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
616     &li.use_explosion_element[3],       FALSE
617   },
618   {
619     EL_PLAYER_4,                        -1,
620     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
621     &li.explosion_element[3],           EL_PLAYER_4
622   },
623   {
624     EL_PLAYER_4,                        -1,
625     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
626     &li.use_initial_inventory[3],       FALSE
627   },
628   {
629     EL_PLAYER_4,                        -1,
630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
631     &li.initial_inventory_size[3],      1
632   },
633   {
634     EL_PLAYER_4,                        -1,
635     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
636     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
638   },
639
640   // (these values are only valid for BD style levels)
641   // (some values for BD style amoeba following below)
642   {
643     EL_BDX_PLAYER,                      -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BDX_PLAYER,                      -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BDX_PLAYER,                      -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BDX_PLAYER,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BDX_PLAYER,                      -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BDX_PLAYER,                      -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BDX_SAND_1,                      -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BDX_SAND_1
677   },
678
679   {
680     EL_BDX_ROCK,                        -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BDX_ROCK_FALLING
683   },
684   {
685     EL_BDX_ROCK,                        -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BDX_ROCK
688   },
689
690   {
691     EL_BDX_DIAMOND,                     -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BDX_DIAMOND,                     -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
699   },
700   {
701     EL_BDX_DIAMOND,                     -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BDX_DIAMOND
704   },
705
706   {
707     EL_BDX_FIREFLY_1,                   -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_1_explodes_to,       EL_BDX_EXPLODING_1
710   },
711
712   {
713     EL_BDX_FIREFLY_2,                   -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BDX_EXPLODING_1
716   },
717
718   {
719     EL_BDX_BUTTERFLY_1,                 -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_1_explodes_to,     EL_BDX_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BDX_BUTTERFLY_2,                 -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BDX_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BDX_STONEFLY,                    -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BDX_ROCK_GROWING_1
734   },
735
736   {
737     EL_BDX_DRAGONFLY,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BDX_EXPLODING_1
740   },
741
742   {
743     EL_BDX_DIAMOND_GROWING_5,   -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BDX_DIAMOND
746   },
747
748   {
749     EL_BDX_BOMB_EXPLODING_4,            -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BDX_WALL
752   },
753
754   {
755     EL_BDX_NITRO_PACK_EXPLODING_4,      -1,
756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
757     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
758   },
759
760   {
761     EL_BDX_EXPLODING_5,                 -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BDX_MAGIC_WALL,                  -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BDX_MAGIC_WALL,                  -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BDX_MAGIC_WALL,                  -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BDX_MAGIC_WALL,                  -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BDX_MAGIC_WALL,                  -1,
788     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_time,             999
790   },
791   {
792     EL_BDX_MAGIC_WALL,                  -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_diamond_to,       EL_BDX_ROCK_FALLING
795   },
796   {
797     EL_BDX_MAGIC_WALL,                  -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_rock_to,          EL_BDX_DIAMOND_FALLING
800   },
801   {
802     EL_BDX_MAGIC_WALL,                  -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_mega_rock_to,     EL_BDX_NITRO_PACK_FALLING
805   },
806   {
807     EL_BDX_MAGIC_WALL,                  -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nut_to,           EL_BDX_NUT_FALLING
810   },
811   {
812     EL_BDX_MAGIC_WALL,                  -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_nitro_pack_to,    EL_BDX_MEGA_ROCK_FALLING
815   },
816   {
817     EL_BDX_MAGIC_WALL,                  -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
820   },
821   {
822     EL_BDX_MAGIC_WALL,                  -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
824     &li.bd_magic_wall_flying_rock_to,   EL_BDX_FLYING_DIAMOND_FLYING
825   },
826
827   {
828     EL_BDX_CLOCK,                       -1,
829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
830     &li.bd_clock_extra_time,            30
831   },
832
833   {
834     EL_BDX_VOODOO_DOLL,                 -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.bd_voodoo_collects_diamonds,    FALSE
837   },
838   {
839     EL_BDX_VOODOO_DOLL,                 -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
841     &li.bd_voodoo_hurt_kills_player,    FALSE
842   },
843   {
844     EL_BDX_VOODOO_DOLL,                 -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
846     &li.bd_voodoo_dies_by_rock,         FALSE
847   },
848   {
849     EL_BDX_VOODOO_DOLL,                 -1,
850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
851     &li.bd_voodoo_vanish_by_explosion,  TRUE
852   },
853   {
854     EL_BDX_VOODOO_DOLL,                 -1,
855     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
856     &li.bd_voodoo_penalty_time,         30
857   },
858
859   {
860     EL_BDX_SLIME,                       -1,
861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
862     &li.bd_slime_is_predictable,        TRUE
863   },
864   {
865     EL_BDX_SLIME,                       -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
867     &li.bd_slime_permeability_rate,     100
868   },
869   {
870     EL_BDX_SLIME,                       -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
872     &li.bd_slime_permeability_bits_c64, 0
873   },
874   {
875     EL_BDX_SLIME,                       -1,
876     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
877     &li.bd_slime_random_seed_c64,       -1
878   },
879   {
880     EL_BDX_SLIME,                       -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
882     &li.bd_slime_eats_element_1,        EL_BDX_DIAMOND
883   },
884   {
885     EL_BDX_SLIME,                       -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
887     &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
888   },
889   {
890     EL_BDX_SLIME,                       -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
892     &li.bd_slime_eats_element_2,        EL_BDX_ROCK
893   },
894   {
895     EL_BDX_SLIME,                       -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
897     &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
898   },
899   {
900     EL_BDX_SLIME,                       -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
902     &li.bd_slime_eats_element_3,        EL_BDX_NUT
903   },
904   {
905     EL_BDX_SLIME,                       -1,
906     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
907     &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
908   },
909
910   {
911     EL_BDX_ACID,                        -1,
912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
913     &li.bd_acid_eats_element,           EL_BDX_SAND_1
914   },
915   {
916     EL_BDX_ACID,                        -1,
917     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
918     &li.bd_acid_spread_rate,            3
919   },
920   {
921     EL_BDX_ACID,                        -1,
922     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
923     &li.bd_acid_turns_to_element,       EL_BDX_EXPLODING_3
924   },
925
926   {
927     EL_BDX_BITER,                       -1,
928     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
929     &li.bd_biter_move_delay,            0
930   },
931   {
932     EL_BDX_BITER,                       -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_biter_eats_element,          EL_BDX_DIAMOND
935   },
936
937   {
938     EL_BDX_BLADDER,                     -1,
939     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
940     &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
941   },
942
943   {
944     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
946     &li.bd_change_expanding_wall,       FALSE
947   },
948   {
949     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
951     &li.bd_expanding_wall_looks_like,   EL_BDX_WALL
952   },
953
954   {
955     EL_BDX_REPLICATOR,                  -1,
956     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
957     &li.bd_replicators_active,          TRUE
958   },
959   {
960     EL_BDX_REPLICATOR,                  -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
962     &li.bd_replicator_create_delay,     4
963   },
964
965   {
966     EL_BDX_CONVEYOR_LEFT,               -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
968     &li.bd_conveyor_belts_active,       TRUE
969   },
970   {
971     EL_BDX_CONVEYOR_LEFT,               -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
973     &li.bd_conveyor_belts_changed,      FALSE
974   },
975
976   {
977     EL_BDX_WATER,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.bd_water_cannot_flow_down,      FALSE
980   },
981
982   {
983     EL_BDX_NUT,                         -1,
984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
985     &li.bd_nut_content,                 EL_BDX_NUT_BREAKING_1
986   },
987
988   {
989     EL_BDX_PNEUMATIC_HAMMER,            -1,
990     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
991     &li.bd_hammer_walls_break_delay,    5
992   },
993   {
994     EL_BDX_PNEUMATIC_HAMMER,            -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
996     &li.bd_hammer_walls_reappear,       FALSE
997   },
998   {
999     EL_BDX_PNEUMATIC_HAMMER,            -1,
1000     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1001     &li.bd_hammer_walls_reappear_delay, 100
1002   },
1003
1004   {
1005     EL_BDX_ROCKET_LAUNCHER,             -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1007     &li.bd_infinite_rockets,            FALSE
1008   },
1009
1010   {
1011     EL_BDX_SKELETON,                    -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1013     &li.bd_num_skeletons_needed_for_pot, 5
1014   },
1015   {
1016     EL_BDX_SKELETON,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1018     &li.bd_skeleton_worth_num_diamonds, 0
1019   },
1020
1021   {
1022     EL_BDX_CREATURE_SWITCH,             -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1024     &li.bd_creatures_start_backwards,   FALSE
1025   },
1026   {
1027     EL_BDX_CREATURE_SWITCH,             -1,
1028     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1029     &li.bd_creatures_turn_on_hatching,  FALSE
1030   },
1031   {
1032     EL_BDX_CREATURE_SWITCH,             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.bd_creatures_auto_turn_delay,   0
1035   },
1036
1037   {
1038     EL_BDX_GRAVITY_SWITCH,              -1,
1039     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1040     &li.bd_gravity_direction,           GD_MV_DOWN
1041   },
1042   {
1043     EL_BDX_GRAVITY_SWITCH,              -1,
1044     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1045     &li.bd_gravity_switch_active,       FALSE
1046   },
1047   {
1048     EL_BDX_GRAVITY_SWITCH,              -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1050     &li.bd_gravity_switch_delay,        10
1051   },
1052   {
1053     EL_BDX_GRAVITY_SWITCH,              -1,
1054     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1055     &li.bd_gravity_affects_all,         TRUE
1056   },
1057
1058   // (the following values are related to various game elements)
1059
1060   {
1061     EL_EMERALD,                         -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_EMERALD],              10
1064   },
1065
1066   {
1067     EL_DIAMOND,                         -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_DIAMOND],              10
1070   },
1071
1072   {
1073     EL_BUG,                             -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.score[SC_BUG],                  10
1076   },
1077
1078   {
1079     EL_SPACESHIP,                       -1,
1080     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1081     &li.score[SC_SPACESHIP],            10
1082   },
1083
1084   {
1085     EL_PACMAN,                          -1,
1086     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1087     &li.score[SC_PACMAN],               10
1088   },
1089
1090   {
1091     EL_NUT,                             -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1093     &li.score[SC_NUT],                  10
1094   },
1095
1096   {
1097     EL_DYNAMITE,                        -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.score[SC_DYNAMITE],             10
1100   },
1101
1102   {
1103     EL_KEY_1,                           -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.score[SC_KEY],                  10
1106   },
1107
1108   {
1109     EL_PEARL,                           -1,
1110     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1111     &li.score[SC_PEARL],                10
1112   },
1113
1114   {
1115     EL_CRYSTAL,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.score[SC_CRYSTAL],              10
1118   },
1119
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1123     &li.amoeba_content,                 EL_DIAMOND
1124   },
1125   {
1126     EL_BD_AMOEBA,                       -1,
1127     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1128     &li.amoeba_speed,                   10
1129   },
1130   {
1131     EL_BD_AMOEBA,                       -1,
1132     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1133     &li.grow_into_diggable,             TRUE
1134   },
1135
1136   {
1137     EL_BDX_AMOEBA_1,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.bd_amoeba_1_threshold_too_big,  200
1140   },
1141   {
1142     EL_BDX_AMOEBA_1,                    -1,
1143     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1144     &li.bd_amoeba_1_slow_growth_time,   200
1145   },
1146   {
1147     EL_BDX_AMOEBA_1,                    -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1149     &li.bd_amoeba_1_content_too_big,    EL_BDX_ROCK
1150   },
1151   {
1152     EL_BDX_AMOEBA_1,                    -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1154     &li.bd_amoeba_1_content_enclosed,   EL_BDX_DIAMOND
1155   },
1156   {
1157     EL_BDX_AMOEBA_1,                    -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1159     &li.bd_amoeba_1_slow_growth_rate,   3
1160   },
1161   {
1162     EL_BDX_AMOEBA_1,                    -1,
1163     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1164     &li.bd_amoeba_1_fast_growth_rate,   25
1165   },
1166   {
1167     EL_BDX_AMOEBA_1,                    -1,
1168     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1169     &li.bd_amoeba_wait_for_hatching,    FALSE
1170   },
1171   {
1172     EL_BDX_AMOEBA_1,                    -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1174     &li.bd_amoeba_start_immediately,    TRUE
1175   },
1176
1177   {
1178     EL_BDX_AMOEBA_2,                    -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1180     &li.bd_amoeba_2_threshold_too_big,  200
1181   },
1182   {
1183     EL_BDX_AMOEBA_2,                    -1,
1184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1185     &li.bd_amoeba_2_slow_growth_time,   200
1186   },
1187   {
1188     EL_BDX_AMOEBA_2,                    -1,
1189     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1190     &li.bd_amoeba_2_content_too_big,    EL_BDX_ROCK
1191   },
1192   {
1193     EL_BDX_AMOEBA_2,                    -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1195     &li.bd_amoeba_2_content_enclosed,   EL_BDX_DIAMOND
1196   },
1197   {
1198     EL_BDX_AMOEBA_2,                    -1,
1199     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1200     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1201   },
1202   {
1203     EL_BDX_AMOEBA_2,                    -1,
1204     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1205     &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1206   },
1207   {
1208     EL_BDX_AMOEBA_2,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1210     &li.bd_amoeba_2_slow_growth_rate,   3
1211   },
1212   {
1213     EL_BDX_AMOEBA_2,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1215     &li.bd_amoeba_2_fast_growth_rate,   25
1216   },
1217   {
1218     EL_BDX_AMOEBA_2,                    -1,
1219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1220     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1221   },
1222
1223   {
1224     EL_YAMYAM,                          -1,
1225     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.yamyam_content,                 EL_ROCK, NULL,
1227     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1228   },
1229   {
1230     EL_YAMYAM,                          -1,
1231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1232     &li.score[SC_YAMYAM],               10
1233   },
1234
1235   {
1236     EL_ROBOT,                           -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.score[SC_ROBOT],                10
1239   },
1240   {
1241     EL_ROBOT,                           -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1243     &li.slurp_score,                    10
1244   },
1245
1246   {
1247     EL_ROBOT_WHEEL,                     -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.time_wheel,                     10
1250   },
1251
1252   {
1253     EL_MAGIC_WALL,                      -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.time_magic_wall,                10
1256   },
1257
1258   {
1259     EL_GAME_OF_LIFE,                    -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.game_of_life[0],                2
1262   },
1263   {
1264     EL_GAME_OF_LIFE,                    -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &li.game_of_life[1],                3
1267   },
1268   {
1269     EL_GAME_OF_LIFE,                    -1,
1270     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1271     &li.game_of_life[2],                3
1272   },
1273   {
1274     EL_GAME_OF_LIFE,                    -1,
1275     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1276     &li.game_of_life[3],                3
1277   },
1278   {
1279     EL_GAME_OF_LIFE,                    -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1281     &li.use_life_bugs,                  FALSE
1282   },
1283
1284   {
1285     EL_BIOMAZE,                         -1,
1286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1287     &li.biomaze[0],                     2
1288   },
1289   {
1290     EL_BIOMAZE,                         -1,
1291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1292     &li.biomaze[1],                     3
1293   },
1294   {
1295     EL_BIOMAZE,                         -1,
1296     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1297     &li.biomaze[2],                     3
1298   },
1299   {
1300     EL_BIOMAZE,                         -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1302     &li.biomaze[3],                     3
1303   },
1304
1305   {
1306     EL_TIMEGATE_SWITCH,                 -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_timegate,                  10
1309   },
1310
1311   {
1312     EL_LIGHT_SWITCH_ACTIVE,             -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.time_light,                     10
1315   },
1316
1317   {
1318     EL_SHIELD_NORMAL,                   -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.shield_normal_time,             10
1321   },
1322   {
1323     EL_SHIELD_NORMAL,                   -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.score[SC_SHIELD],               10
1326   },
1327
1328   {
1329     EL_SHIELD_DEADLY,                   -1,
1330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1331     &li.shield_deadly_time,             10
1332   },
1333   {
1334     EL_SHIELD_DEADLY,                   -1,
1335     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1336     &li.score[SC_SHIELD],               10
1337   },
1338
1339   {
1340     EL_EXTRA_TIME,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1342     &li.extra_time,                     10
1343   },
1344   {
1345     EL_EXTRA_TIME,                      -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1347     &li.extra_time_score,               10
1348   },
1349
1350   {
1351     EL_TIME_ORB_FULL,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.time_orb_time,                  10
1354   },
1355   {
1356     EL_TIME_ORB_FULL,                   -1,
1357     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1358     &li.use_time_orb_bug,               FALSE
1359   },
1360
1361   {
1362     EL_SPRING,                          -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.use_spring_bug,                 FALSE
1365   },
1366
1367   {
1368     EL_EMC_ANDROID,                     -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.android_move_time,              10
1371   },
1372   {
1373     EL_EMC_ANDROID,                     -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1375     &li.android_clone_time,             10
1376   },
1377   {
1378     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1379     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.android_clone_element[0],       EL_EMPTY, NULL,
1381     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1382   },
1383   {
1384     EL_EMC_ANDROID,                     -1,
1385     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1386     &li.android_clone_element[0],       EL_EMPTY, NULL,
1387     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1388   },
1389
1390   {
1391     EL_EMC_LENSES,                      -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1393     &li.lenses_score,                   10
1394   },
1395   {
1396     EL_EMC_LENSES,                      -1,
1397     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1398     &li.lenses_time,                    10
1399   },
1400
1401   {
1402     EL_EMC_MAGNIFIER,                   -1,
1403     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1404     &li.magnify_score,                  10
1405   },
1406   {
1407     EL_EMC_MAGNIFIER,                   -1,
1408     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1409     &li.magnify_time,                   10
1410   },
1411
1412   {
1413     EL_EMC_MAGIC_BALL,                  -1,
1414     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1415     &li.ball_time,                      10
1416   },
1417   {
1418     EL_EMC_MAGIC_BALL,                  -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.ball_random,                    FALSE
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL,                  -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.ball_active_initial,            FALSE
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL,                  -1,
1429     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1430     &li.ball_content,                   EL_EMPTY, NULL,
1431     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1432   },
1433
1434   {
1435     EL_SOKOBAN_FIELD_EMPTY,             -1,
1436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1437     &li.sb_fields_needed,               TRUE
1438   },
1439
1440   {
1441     EL_SOKOBAN_OBJECT,                  -1,
1442     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1443     &li.sb_objects_needed,              TRUE
1444   },
1445
1446   {
1447     EL_MM_MCDUFFIN,                     -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &li.mm_laser_red,                   FALSE
1450   },
1451   {
1452     EL_MM_MCDUFFIN,                     -1,
1453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1454     &li.mm_laser_green,                 FALSE
1455   },
1456   {
1457     EL_MM_MCDUFFIN,                     -1,
1458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1459     &li.mm_laser_blue,                  TRUE
1460   },
1461
1462   {
1463     EL_DF_LASER,                        -1,
1464     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1465     &li.df_laser_red,                   TRUE
1466   },
1467   {
1468     EL_DF_LASER,                        -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1470     &li.df_laser_green,                 TRUE
1471   },
1472   {
1473     EL_DF_LASER,                        -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1475     &li.df_laser_blue,                  FALSE
1476   },
1477
1478   {
1479     EL_MM_FUSE_ACTIVE,                  -1,
1480     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1481     &li.mm_time_fuse,                   25
1482   },
1483   {
1484     EL_MM_BOMB,                         -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1486     &li.mm_time_bomb,                   75
1487   },
1488
1489   {
1490     EL_MM_GRAY_BALL,                    -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1492     &li.mm_time_ball,                   75
1493   },
1494   {
1495     EL_MM_GRAY_BALL,                    -1,
1496     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1497     &li.mm_ball_choice_mode,            ANIM_RANDOM
1498   },
1499   {
1500     EL_MM_GRAY_BALL,                    -1,
1501     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1502     &li.mm_ball_content,                EL_EMPTY, NULL,
1503     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1504   },
1505   {
1506     EL_MM_GRAY_BALL,                    -1,
1507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1508     &li.rotate_mm_ball_content,         TRUE
1509   },
1510   {
1511     EL_MM_GRAY_BALL,                    -1,
1512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1513     &li.explode_mm_ball,                FALSE
1514   },
1515
1516   {
1517     EL_MM_STEEL_BLOCK,                  -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1519     &li.mm_time_block,                  75
1520   },
1521   {
1522     EL_MM_LIGHTBALL,                    -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1524     &li.score[SC_ELEM_BONUS],           10
1525   },
1526
1527   {
1528     -1,                                 -1,
1529     -1,                                 -1,
1530     NULL,                               -1
1531   }
1532 };
1533
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1535 {
1536   {
1537     -1,                                 -1,
1538     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1539     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1544     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1550     &xx_envelope.autowrap,              FALSE
1551   },
1552   {
1553     -1,                                 -1,
1554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1555     &xx_envelope.centered,              FALSE
1556   },
1557
1558   {
1559     -1,                                 -1,
1560     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1561     &xx_envelope.text,                  -1, NULL,
1562     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1563     &xx_default_string_empty[0]
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     -1,                                 -1,
1569     NULL,                               -1
1570   }
1571 };
1572
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1574 {
1575   {
1576     -1,                                 -1,
1577     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1578     &xx_ei.description[0],              -1,
1579     &yy_ei.description[0],
1580     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1581     &xx_default_description[0]
1582   },
1583
1584   {
1585     -1,                                 -1,
1586     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1587     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1589   },
1590 #if ENABLE_RESERVED_CODE
1591   // (reserved for later use)
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1595     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1597   },
1598 #endif
1599
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1603     &xx_ei.use_gfx_element,             FALSE,
1604     &yy_ei.use_gfx_element
1605   },
1606   {
1607     -1,                                 -1,
1608     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1609     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1610     &yy_ei.gfx_element_initial
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1616     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1617     &yy_ei.access_direction
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1623     &xx_ei.collect_score_initial,       10,
1624     &yy_ei.collect_score_initial
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1629     &xx_ei.collect_count_initial,       1,
1630     &yy_ei.collect_count_initial
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1636     &xx_ei.ce_value_fixed_initial,      0,
1637     &yy_ei.ce_value_fixed_initial
1638   },
1639   {
1640     -1,                                 -1,
1641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1642     &xx_ei.ce_value_random_initial,     0,
1643     &yy_ei.ce_value_random_initial
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1648     &xx_ei.use_last_ce_value,           FALSE,
1649     &yy_ei.use_last_ce_value
1650   },
1651
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1655     &xx_ei.push_delay_fixed,            8,
1656     &yy_ei.push_delay_fixed
1657   },
1658   {
1659     -1,                                 -1,
1660     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1661     &xx_ei.push_delay_random,           8,
1662     &yy_ei.push_delay_random
1663   },
1664   {
1665     -1,                                 -1,
1666     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1667     &xx_ei.drop_delay_fixed,            0,
1668     &yy_ei.drop_delay_fixed
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1673     &xx_ei.drop_delay_random,           0,
1674     &yy_ei.drop_delay_random
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1679     &xx_ei.move_delay_fixed,            0,
1680     &yy_ei.move_delay_fixed
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1685     &xx_ei.move_delay_random,           0,
1686     &yy_ei.move_delay_random
1687   },
1688   {
1689     -1,                                 -1,
1690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1691     &xx_ei.step_delay_fixed,            0,
1692     &yy_ei.step_delay_fixed
1693   },
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1697     &xx_ei.step_delay_random,           0,
1698     &yy_ei.step_delay_random
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1704     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1705     &yy_ei.move_pattern
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1710     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1711     &yy_ei.move_direction_initial
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1716     &xx_ei.move_stepsize,               TILEX / 8,
1717     &yy_ei.move_stepsize
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1723     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1724     &yy_ei.move_enter_element
1725   },
1726   {
1727     -1,                                 -1,
1728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1729     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1730     &yy_ei.move_leave_element
1731   },
1732   {
1733     -1,                                 -1,
1734     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1735     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1736     &yy_ei.move_leave_type
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1742     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1743     &yy_ei.slippery_type
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1749     &xx_ei.explosion_type,              EXPLODES_3X3,
1750     &yy_ei.explosion_type
1751   },
1752   {
1753     -1,                                 -1,
1754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1755     &xx_ei.explosion_delay,             16,
1756     &yy_ei.explosion_delay
1757   },
1758   {
1759     -1,                                 -1,
1760     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1761     &xx_ei.ignition_delay,              8,
1762     &yy_ei.ignition_delay
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1768     &xx_ei.content,                     EL_EMPTY_SPACE,
1769     &yy_ei.content,
1770     &xx_num_contents,                   1, 1
1771   },
1772
1773   // ---------- "num_change_pages" must be the last entry ---------------------
1774
1775   {
1776     -1,                                 SAVE_CONF_ALWAYS,
1777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1778     &xx_ei.num_change_pages,            1,
1779     &yy_ei.num_change_pages
1780   },
1781
1782   {
1783     -1,                                 -1,
1784     -1,                                 -1,
1785     NULL,                               -1,
1786     NULL
1787   }
1788 };
1789
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1791 {
1792   // ---------- "current_change_page" must be the first entry -----------------
1793
1794   {
1795     -1,                                 SAVE_CONF_ALWAYS,
1796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1797     &xx_current_change_page,            -1
1798   },
1799
1800   // ---------- (the remaining entries can be in any order) -------------------
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1805     &xx_change.can_change,              FALSE
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1811     &xx_event_bits[0],                  0
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1816     &xx_event_bits[1],                  0
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1822     &xx_change.trigger_player,          CH_PLAYER_ANY
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1827     &xx_change.trigger_side,            CH_SIDE_ANY
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1832     &xx_change.trigger_page,            CH_PAGE_ANY
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1838     &xx_change.target_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1844     &xx_change.delay_fixed,             0
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1849     &xx_change.delay_random,            0
1850   },
1851   {
1852     -1,                                 -1,
1853     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1854     &xx_change.delay_frames,            FRAMES_PER_SECOND
1855   },
1856
1857   {
1858     -1,                                 -1,
1859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1860     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861   },
1862
1863   {
1864     -1,                                 -1,
1865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1866     &xx_change.explode,                 FALSE
1867   },
1868   {
1869     -1,                                 -1,
1870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1871     &xx_change.use_target_content,      FALSE
1872   },
1873   {
1874     -1,                                 -1,
1875     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1876     &xx_change.only_if_complete,        FALSE
1877   },
1878   {
1879     -1,                                 -1,
1880     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1881     &xx_change.use_random_replace,      FALSE
1882   },
1883   {
1884     -1,                                 -1,
1885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1886     &xx_change.random_percentage,       100
1887   },
1888   {
1889     -1,                                 -1,
1890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1891     &xx_change.replace_when,            CP_WHEN_EMPTY
1892   },
1893
1894   {
1895     -1,                                 -1,
1896     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1897     &xx_change.has_action,              FALSE
1898   },
1899   {
1900     -1,                                 -1,
1901     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1902     &xx_change.action_type,             CA_NO_ACTION
1903   },
1904   {
1905     -1,                                 -1,
1906     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1907     &xx_change.action_mode,             CA_MODE_UNDEFINED
1908   },
1909   {
1910     -1,                                 -1,
1911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1912     &xx_change.action_arg,              CA_ARG_UNDEFINED
1913   },
1914
1915   {
1916     -1,                                 -1,
1917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1918     &xx_change.action_element,          EL_EMPTY_SPACE
1919   },
1920
1921   {
1922     -1,                                 -1,
1923     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1924     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1925     &xx_num_contents,                   1, 1
1926   },
1927
1928   {
1929     -1,                                 -1,
1930     -1,                                 -1,
1931     NULL,                               -1
1932   }
1933 };
1934
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1936 {
1937   {
1938     -1,                                 -1,
1939     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1940     &xx_ei.description[0],              -1, NULL,
1941     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1942     &xx_default_description[0]
1943   },
1944
1945   {
1946     -1,                                 -1,
1947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1948     &xx_ei.use_gfx_element,             FALSE
1949   },
1950   {
1951     -1,                                 -1,
1952     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1953     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1954   },
1955
1956   {
1957     -1,                                 -1,
1958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1959     &xx_group.choice_mode,              ANIM_RANDOM
1960   },
1961
1962   {
1963     -1,                                 -1,
1964     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1965     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1966     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1967   },
1968
1969   {
1970     -1,                                 -1,
1971     -1,                                 -1,
1972     NULL,                               -1
1973   }
1974 };
1975
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1977 {
1978   {
1979     -1,                                 -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1981     &xx_ei.use_gfx_element,             FALSE
1982   },
1983   {
1984     -1,                                 -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1986     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1987   },
1988
1989   {
1990     -1,                                 -1,
1991     -1,                                 -1,
1992     NULL,                               -1
1993   }
1994 };
1995
1996 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1997 {
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
2001     &li.block_snap_field,               TRUE
2002   },
2003   {
2004     EL_PLAYER_1,                        -1,
2005     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2006     &li.continuous_snapping,            TRUE
2007   },
2008   {
2009     EL_PLAYER_1,                        -1,
2010     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2011     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2012   },
2013   {
2014     EL_PLAYER_1,                        -1,
2015     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2016     &li.use_start_element[0],           FALSE
2017   },
2018   {
2019     EL_PLAYER_1,                        -1,
2020     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2021     &li.start_element[0],               EL_PLAYER_1
2022   },
2023   {
2024     EL_PLAYER_1,                        -1,
2025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2026     &li.use_artwork_element[0],         FALSE
2027   },
2028   {
2029     EL_PLAYER_1,                        -1,
2030     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2031     &li.artwork_element[0],             EL_PLAYER_1
2032   },
2033   {
2034     EL_PLAYER_1,                        -1,
2035     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2036     &li.use_explosion_element[0],       FALSE
2037   },
2038   {
2039     EL_PLAYER_1,                        -1,
2040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2041     &li.explosion_element[0],           EL_PLAYER_1
2042   },
2043
2044   {
2045     -1,                                 -1,
2046     -1,                                 -1,
2047     NULL,                               -1
2048   }
2049 };
2050
2051 static struct
2052 {
2053   int filetype;
2054   char *id;
2055 }
2056 filetype_id_list[] =
2057 {
2058   { LEVEL_FILE_TYPE_RND,        "RND"   },
2059   { LEVEL_FILE_TYPE_BD,         "BD"    },
2060   { LEVEL_FILE_TYPE_EM,         "EM"    },
2061   { LEVEL_FILE_TYPE_SP,         "SP"    },
2062   { LEVEL_FILE_TYPE_DX,         "DX"    },
2063   { LEVEL_FILE_TYPE_SB,         "SB"    },
2064   { LEVEL_FILE_TYPE_DC,         "DC"    },
2065   { LEVEL_FILE_TYPE_MM,         "MM"    },
2066   { LEVEL_FILE_TYPE_MM,         "DF"    },
2067   { -1,                         NULL    },
2068 };
2069
2070
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2074
2075 static boolean check_special_flags(char *flag)
2076 {
2077   if (strEqual(options.special_flags, flag) ||
2078       strEqual(leveldir_current->special_flags, flag))
2079     return TRUE;
2080
2081   return FALSE;
2082 }
2083
2084 static struct DateInfo getCurrentDate(void)
2085 {
2086   time_t epoch_seconds = time(NULL);
2087   struct tm *now = localtime(&epoch_seconds);
2088   struct DateInfo date;
2089
2090   date.year  = now->tm_year + 1900;
2091   date.month = now->tm_mon  + 1;
2092   date.day   = now->tm_mday;
2093
2094   date.src   = DATE_SRC_CLOCK;
2095
2096   return date;
2097 }
2098
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2100 {
2101   int i;
2102
2103   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104     change->has_event[i] = FALSE;
2105 }
2106
2107 static void resetEventBits(void)
2108 {
2109   int i;
2110
2111   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112     xx_event_bits[i] = 0;
2113 }
2114
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2116 {
2117   int i;
2118
2119   /* important: only change event flag if corresponding event bit is set
2120      (this is because all xx_event_bits[] values are loaded separately,
2121      and all xx_event_bits[] values are set back to zero before loading
2122      another value xx_event_bits[x] (each value representing 32 flags)) */
2123
2124   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126       change->has_event[i] = TRUE;
2127 }
2128
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2130 {
2131   int i;
2132
2133   /* in contrast to the above function setEventFlagsFromEventBits(), it
2134      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135      depending on the corresponding change->has_event[i] values here, as
2136      all xx_event_bits[] values are reset in resetEventBits() before */
2137
2138   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139     if (change->has_event[i])
2140       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2141 }
2142
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2144 {
2145   static char description[MAX_ELEMENT_NAME_LEN + 1];
2146   char *default_description = (ei->custom_description != NULL ?
2147                                ei->custom_description :
2148                                ei->editor_description);
2149   int i;
2150
2151   // always start with reliable default values
2152   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153     description[i] = '\0';
2154
2155   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2157
2158   return &description[0];
2159 }
2160
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2162 {
2163   char *default_description = getDefaultElementDescription(ei);
2164   int i;
2165
2166   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167     ei->description[i] = default_description[i];
2168 }
2169
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2171 {
2172   int i;
2173
2174   for (i = 0; conf[i].data_type != -1; i++)
2175   {
2176     int default_value = conf[i].default_value;
2177     int data_type = conf[i].data_type;
2178     int conf_type = conf[i].conf_type;
2179     int byte_mask = conf_type & CONF_MASK_BYTES;
2180
2181     if (byte_mask == CONF_MASK_MULTI_BYTES)
2182     {
2183       int default_num_entities = conf[i].default_num_entities;
2184       int max_num_entities = conf[i].max_num_entities;
2185
2186       *(int *)(conf[i].num_entities) = default_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *default_string = conf[i].default_string;
2191         char *string = (char *)(conf[i].value);
2192
2193         strncpy(string, default_string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array = (int *)(conf[i].value);
2198         int j;
2199
2200         for (j = 0; j < max_num_entities; j++)
2201           element_array[j] = default_value;
2202       }
2203       else if (data_type == TYPE_CONTENT_LIST)
2204       {
2205         struct Content *content = (struct Content *)(conf[i].value);
2206         int c, x, y;
2207
2208         for (c = 0; c < max_num_entities; c++)
2209           for (y = 0; y < 3; y++)
2210             for (x = 0; x < 3; x++)
2211               content[c].e[x][y] = default_value;
2212       }
2213     }
2214     else        // constant size configuration data (1, 2 or 4 bytes)
2215     {
2216       if (data_type == TYPE_BOOLEAN)
2217         *(boolean *)(conf[i].value) = default_value;
2218       else
2219         *(int *)    (conf[i].value) = default_value;
2220     }
2221   }
2222 }
2223
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2225 {
2226   int i;
2227
2228   for (i = 0; conf[i].data_type != -1; i++)
2229   {
2230     int data_type = conf[i].data_type;
2231     int conf_type = conf[i].conf_type;
2232     int byte_mask = conf_type & CONF_MASK_BYTES;
2233
2234     if (byte_mask == CONF_MASK_MULTI_BYTES)
2235     {
2236       int max_num_entities = conf[i].max_num_entities;
2237
2238       if (data_type == TYPE_STRING)
2239       {
2240         char *string      = (char *)(conf[i].value);
2241         char *string_copy = (char *)(conf[i].value_copy);
2242
2243         strncpy(string_copy, string, max_num_entities);
2244       }
2245       else if (data_type == TYPE_ELEMENT_LIST)
2246       {
2247         int *element_array      = (int *)(conf[i].value);
2248         int *element_array_copy = (int *)(conf[i].value_copy);
2249         int j;
2250
2251         for (j = 0; j < max_num_entities; j++)
2252           element_array_copy[j] = element_array[j];
2253       }
2254       else if (data_type == TYPE_CONTENT_LIST)
2255       {
2256         struct Content *content      = (struct Content *)(conf[i].value);
2257         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2258         int c, x, y;
2259
2260         for (c = 0; c < max_num_entities; c++)
2261           for (y = 0; y < 3; y++)
2262             for (x = 0; x < 3; x++)
2263               content_copy[c].e[x][y] = content[c].e[x][y];
2264       }
2265     }
2266     else        // constant size configuration data (1, 2 or 4 bytes)
2267     {
2268       if (data_type == TYPE_BOOLEAN)
2269         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2270       else
2271         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2272     }
2273   }
2274 }
2275
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2277 {
2278   int i;
2279
2280   xx_ei = *ei_from;     // copy element data into temporary buffer
2281   yy_ei = *ei_to;       // copy element data into temporary buffer
2282
2283   copyConfigFromConfigList(chunk_config_CUSX_base);
2284
2285   *ei_from = xx_ei;
2286   *ei_to   = yy_ei;
2287
2288   // ---------- reinitialize and copy change pages ----------
2289
2290   ei_to->num_change_pages = ei_from->num_change_pages;
2291   ei_to->current_change_page = ei_from->current_change_page;
2292
2293   setElementChangePages(ei_to, ei_to->num_change_pages);
2294
2295   for (i = 0; i < ei_to->num_change_pages; i++)
2296     ei_to->change_page[i] = ei_from->change_page[i];
2297
2298   // ---------- copy group element info ----------
2299   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2300     *ei_to->group = *ei_from->group;
2301
2302   // mark this custom element as modified
2303   ei_to->modified_settings = TRUE;
2304 }
2305
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2307 {
2308   int change_page_size = sizeof(struct ElementChangeInfo);
2309
2310   ei->num_change_pages = MAX(1, change_pages);
2311
2312   ei->change_page =
2313     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2314
2315   if (ei->current_change_page >= ei->num_change_pages)
2316     ei->current_change_page = ei->num_change_pages - 1;
2317
2318   ei->change = &ei->change_page[ei->current_change_page];
2319 }
2320
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2322 {
2323   xx_change = *change;          // copy change data into temporary buffer
2324
2325   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2326
2327   *change = xx_change;
2328
2329   resetEventFlags(change);
2330
2331   change->direct_action = 0;
2332   change->other_action = 0;
2333
2334   change->pre_change_function = NULL;
2335   change->change_function = NULL;
2336   change->post_change_function = NULL;
2337 }
2338
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2340 {
2341   boolean add_border = FALSE;
2342   int x1 = 0;
2343   int y1 = 0;
2344   int x2 = STD_LEV_FIELDX - 1;
2345   int y2 = STD_LEV_FIELDY - 1;
2346   int i, x, y;
2347
2348   li = *level;          // copy level data into temporary buffer
2349   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350   *level = li;          // copy temporary buffer back to level data
2351
2352   setLevelInfoToDefaults_BD();
2353   setLevelInfoToDefaults_EM();
2354   setLevelInfoToDefaults_SP();
2355   setLevelInfoToDefaults_MM();
2356
2357   level->native_bd_level = &native_bd_level;
2358   level->native_em_level = &native_em_level;
2359   level->native_sp_level = &native_sp_level;
2360   level->native_mm_level = &native_mm_level;
2361
2362   level->file_version = FILE_VERSION_ACTUAL;
2363   level->game_version = GAME_VERSION_ACTUAL;
2364
2365   level->creation_date = getCurrentDate();
2366
2367   level->encoding_16bit_field  = TRUE;
2368   level->encoding_16bit_yamyam = TRUE;
2369   level->encoding_16bit_amoeba = TRUE;
2370
2371   // clear level name and level author string buffers
2372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373     level->name[i] = '\0';
2374   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375     level->author[i] = '\0';
2376
2377   // set level name and level author to default values
2378   strcpy(level->name, NAMELESS_LEVEL_NAME);
2379   strcpy(level->author, ANONYMOUS_NAME);
2380
2381   // set default game engine type
2382   level->game_engine_type = setup.default_game_engine_type;
2383
2384   // some game engines should have a default playfield with border elements
2385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2388   {
2389     add_border = TRUE;
2390     x1++;
2391     y1++;
2392     x2--;
2393     y2--;
2394   }
2395
2396   // set level playfield to playable default level with player and exit
2397   for (x = 0; x < MAX_LEV_FIELDX; x++)
2398   {
2399     for (y = 0; y < MAX_LEV_FIELDY; y++)
2400     {
2401       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402                          y == 0 || y == STD_LEV_FIELDY - 1))
2403         level->field[x][y] = getEngineElement(EL_STEELWALL);
2404       else
2405         level->field[x][y] = getEngineElement(EL_SAND);
2406     }
2407   }
2408
2409   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2411
2412   BorderElement = getEngineElement(EL_STEELWALL);
2413
2414   // detect custom elements when loading them
2415   level->file_has_custom_elements = FALSE;
2416
2417   // set random colors for BD style levels according to preferred color type
2418   SetRandomLevelColors_BD(setup.bd_default_color_type);
2419
2420   // set default color type and colors for BD style level colors
2421   SetDefaultLevelColorType_BD();
2422   SetDefaultLevelColors_BD();
2423
2424   // set all bug compatibility flags to "false" => do not emulate this bug
2425   level->use_action_after_change_bug = FALSE;
2426
2427   if (leveldir_current)
2428   {
2429     // try to determine better author name than 'anonymous'
2430     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2431     {
2432       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2434     }
2435     else
2436     {
2437       switch (LEVELCLASS(leveldir_current))
2438       {
2439         case LEVELCLASS_TUTORIAL:
2440           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2441           break;
2442
2443         case LEVELCLASS_CONTRIB:
2444           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2446           break;
2447
2448         case LEVELCLASS_PRIVATE:
2449           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2451           break;
2452
2453         default:
2454           // keep default value
2455           break;
2456       }
2457     }
2458   }
2459 }
2460
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2462 {
2463   static boolean clipboard_elements_initialized = FALSE;
2464   int i;
2465
2466   InitElementPropertiesStatic();
2467
2468   li = *level;          // copy level data into temporary buffer
2469   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470   *level = li;          // copy temporary buffer back to level data
2471
2472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473   {
2474     int element = i;
2475     struct ElementInfo *ei = &element_info[element];
2476
2477     if (element == EL_MM_GRAY_BALL)
2478     {
2479       struct LevelInfo_MM *level_mm = level->native_mm_level;
2480       int j;
2481
2482       for (j = 0; j < level->num_mm_ball_contents; j++)
2483         level->mm_ball_content[j] =
2484           map_element_MM_to_RND(level_mm->ball_content[j]);
2485     }
2486
2487     // never initialize clipboard elements after the very first time
2488     // (to be able to use clipboard elements between several levels)
2489     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2490       continue;
2491
2492     if (IS_ENVELOPE(element))
2493     {
2494       int envelope_nr = element - EL_ENVELOPE_1;
2495
2496       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2497
2498       level->envelope[envelope_nr] = xx_envelope;
2499     }
2500
2501     if (IS_CUSTOM_ELEMENT(element) ||
2502         IS_GROUP_ELEMENT(element) ||
2503         IS_INTERNAL_ELEMENT(element))
2504     {
2505       xx_ei = *ei;      // copy element data into temporary buffer
2506
2507       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508
2509       *ei = xx_ei;
2510     }
2511
2512     setElementChangePages(ei, 1);
2513     setElementChangeInfoToDefaults(ei->change);
2514
2515     if (IS_CUSTOM_ELEMENT(element) ||
2516         IS_GROUP_ELEMENT(element))
2517     {
2518       setElementDescriptionToDefault(ei);
2519
2520       ei->modified_settings = FALSE;
2521     }
2522
2523     if (IS_CUSTOM_ELEMENT(element) ||
2524         IS_INTERNAL_ELEMENT(element))
2525     {
2526       // internal values used in level editor
2527
2528       ei->access_type = 0;
2529       ei->access_layer = 0;
2530       ei->access_protected = 0;
2531       ei->walk_to_action = 0;
2532       ei->smash_targets = 0;
2533       ei->deadliness = 0;
2534
2535       ei->can_explode_by_fire = FALSE;
2536       ei->can_explode_smashed = FALSE;
2537       ei->can_explode_impact = FALSE;
2538
2539       ei->current_change_page = 0;
2540     }
2541
2542     if (IS_GROUP_ELEMENT(element) ||
2543         IS_INTERNAL_ELEMENT(element))
2544     {
2545       struct ElementGroupInfo *group;
2546
2547       // initialize memory for list of elements in group
2548       if (ei->group == NULL)
2549         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2550
2551       group = ei->group;
2552
2553       xx_group = *group;        // copy group data into temporary buffer
2554
2555       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556
2557       *group = xx_group;
2558     }
2559
2560     if (IS_EMPTY_ELEMENT(element) ||
2561         IS_INTERNAL_ELEMENT(element))
2562     {
2563       xx_ei = *ei;              // copy element data into temporary buffer
2564
2565       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2566
2567       *ei = xx_ei;
2568     }
2569   }
2570
2571   clipboard_elements_initialized = TRUE;
2572 }
2573
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575                                    boolean level_info_only,
2576                                    boolean reset_file_status)
2577 {
2578   setLevelInfoToDefaults_Level(level);
2579
2580   if (!level_info_only)
2581     setLevelInfoToDefaults_Elements(level);
2582
2583   if (reset_file_status)
2584   {
2585     level->no_valid_file = FALSE;
2586     level->no_level_file = FALSE;
2587   }
2588
2589   level->changed = FALSE;
2590 }
2591
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2593 {
2594   level_file_info->nr = 0;
2595   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596   level_file_info->packed = FALSE;
2597
2598   setString(&level_file_info->basename, NULL);
2599   setString(&level_file_info->filename, NULL);
2600 }
2601
2602 int getMappedElement_SB(int, boolean);
2603
2604 static void ActivateLevelTemplate(void)
2605 {
2606   int x, y;
2607
2608   if (check_special_flags("load_xsb_to_ces"))
2609   {
2610     // fill smaller playfields with padding "beyond border wall" elements
2611     if (level.fieldx < level_template.fieldx ||
2612         level.fieldy < level_template.fieldy)
2613     {
2614       short field[level.fieldx][level.fieldy];
2615       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2619
2620       // copy old playfield (which is smaller than the visible area)
2621       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622         field[x][y] = level.field[x][y];
2623
2624       // fill new, larger playfield with "beyond border wall" elements
2625       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626         level.field[x][y] = getMappedElement_SB('_', TRUE);
2627
2628       // copy the old playfield to the middle of the new playfield
2629       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2631
2632       level.fieldx = new_fieldx;
2633       level.fieldy = new_fieldy;
2634     }
2635   }
2636
2637   // Currently there is no special action needed to activate the template
2638   // data, because 'element_info' property settings overwrite the original
2639   // level data, while all other variables do not change.
2640
2641   // Exception: 'from_level_template' elements in the original level playfield
2642   // are overwritten with the corresponding elements at the same position in
2643   // playfield from the level template.
2644
2645   for (x = 0; x < level.fieldx; x++)
2646     for (y = 0; y < level.fieldy; y++)
2647       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648         level.field[x][y] = level_template.field[x][y];
2649
2650   if (check_special_flags("load_xsb_to_ces"))
2651   {
2652     struct LevelInfo level_backup = level;
2653
2654     // overwrite all individual level settings from template level settings
2655     level = level_template;
2656
2657     // restore level file info
2658     level.file_info = level_backup.file_info;
2659
2660     // restore playfield size
2661     level.fieldx = level_backup.fieldx;
2662     level.fieldy = level_backup.fieldy;
2663
2664     // restore playfield content
2665     for (x = 0; x < level.fieldx; x++)
2666       for (y = 0; y < level.fieldy; y++)
2667         level.field[x][y] = level_backup.field[x][y];
2668
2669     // restore name and author from individual level
2670     strcpy(level.name,   level_backup.name);
2671     strcpy(level.author, level_backup.author);
2672
2673     // restore flag "use_custom_template"
2674     level.use_custom_template = level_backup.use_custom_template;
2675   }
2676 }
2677
2678 boolean isLevelsetFilename_BD(char *filename)
2679 {
2680   return (strSuffixLower(filename, ".bd") ||
2681           strSuffixLower(filename, ".bdr") ||
2682           strSuffixLower(filename, ".brc") ||
2683           strSuffixLower(filename, ".gds"));
2684 }
2685
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2687 {
2688   // check for native BD level file extensions
2689   if (!isLevelsetFilename_BD(basename))
2690     return FALSE;
2691
2692   // check for standard single-level BD files (like "001.bd")
2693   if (strSuffixLower(basename, ".bd") &&
2694       strlen(basename) == 6 &&
2695       basename[0] >= '0' && basename[0] <= '9' &&
2696       basename[1] >= '0' && basename[1] <= '9' &&
2697       basename[2] >= '0' && basename[2] <= '9')
2698     return FALSE;
2699
2700   // this is a level package in native BD file format
2701   return TRUE;
2702 }
2703
2704 static char *getLevelFilenameFromBasename(char *basename)
2705 {
2706   static char *filename = NULL;
2707
2708   checked_free(filename);
2709
2710   filename = getPath2(getCurrentLevelDir(), basename);
2711
2712   return filename;
2713 }
2714
2715 static int getFileTypeFromBasename(char *basename)
2716 {
2717   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2718
2719   static char *filename = NULL;
2720   struct stat file_status;
2721
2722   // ---------- try to determine file type from filename ----------
2723
2724   // check for typical filename of a Supaplex level package file
2725   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726     return LEVEL_FILE_TYPE_SP;
2727
2728   // check for typical filename of a Diamond Caves II level package file
2729   if (strSuffixLower(basename, ".dc") ||
2730       strSuffixLower(basename, ".dc2"))
2731     return LEVEL_FILE_TYPE_DC;
2732
2733   // check for typical filename of a Sokoban level package file
2734   if (strSuffixLower(basename, ".xsb") &&
2735       strchr(basename, '%') == NULL)
2736     return LEVEL_FILE_TYPE_SB;
2737
2738   // check for typical filename of a Boulder Dash (GDash) level package file
2739   if (checkForPackageFromBasename_BD(basename))
2740     return LEVEL_FILE_TYPE_BD;
2741
2742   // ---------- try to determine file type from filesize ----------
2743
2744   checked_free(filename);
2745   filename = getPath2(getCurrentLevelDir(), basename);
2746
2747   if (stat(filename, &file_status) == 0)
2748   {
2749     // check for typical filesize of a Supaplex level package file
2750     if (file_status.st_size == 170496)
2751       return LEVEL_FILE_TYPE_SP;
2752   }
2753
2754   return LEVEL_FILE_TYPE_UNKNOWN;
2755 }
2756
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2758 {
2759   File *file;
2760
2761   if ((file = openFile(filename, MODE_READ)))
2762   {
2763     char chunk_name[CHUNK_ID_LEN + 1];
2764
2765     getFileChunkBE(file, chunk_name, NULL);
2766
2767     if (strEqual(chunk_name, "MMII") ||
2768         strEqual(chunk_name, "MIRR"))
2769       type = LEVEL_FILE_TYPE_MM;
2770
2771     closeFile(file);
2772   }
2773
2774   return type;
2775 }
2776
2777 static boolean checkForPackageFromBasename(char *basename)
2778 {
2779   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2781
2782   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2783 }
2784
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788
2789   if (nr < 0)
2790     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2791   else
2792     sprintf(basename, "%03d.%s", nr, extension);
2793
2794   return basename;
2795 }
2796
2797 static char *getSingleLevelBasename(int nr)
2798 {
2799   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2800 }
2801
2802 static char *getPackedLevelBasename(int type)
2803 {
2804   static char basename[MAX_FILENAME_LEN];
2805   char *directory = getCurrentLevelDir();
2806   Directory *dir;
2807   DirectoryEntry *dir_entry;
2808
2809   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2810
2811   if ((dir = openDirectory(directory)) == NULL)
2812   {
2813     Warn("cannot read current level directory '%s'", directory);
2814
2815     return basename;
2816   }
2817
2818   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2819   {
2820     char *entry_basename = dir_entry->basename;
2821     int entry_type = getFileTypeFromBasename(entry_basename);
2822
2823     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2824     {
2825       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2826           type == entry_type)
2827       {
2828         strcpy(basename, entry_basename);
2829
2830         break;
2831       }
2832     }
2833   }
2834
2835   closeDirectory(dir);
2836
2837   return basename;
2838 }
2839
2840 static char *getSingleLevelFilename(int nr)
2841 {
2842   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2843 }
2844
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2847 {
2848   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2849 }
2850 #endif
2851
2852 char *getDefaultLevelFilename(int nr)
2853 {
2854   return getSingleLevelFilename(nr);
2855 }
2856
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2859                                                  int type)
2860 {
2861   lfi->type = type;
2862   lfi->packed = FALSE;
2863
2864   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2866 }
2867 #endif
2868
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870                                                  int type, char *format, ...)
2871 {
2872   static char basename[MAX_FILENAME_LEN];
2873   va_list ap;
2874
2875   va_start(ap, format);
2876   vsprintf(basename, format, ap);
2877   va_end(ap);
2878
2879   lfi->type = type;
2880   lfi->packed = FALSE;
2881
2882   setString(&lfi->basename, basename);
2883   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2884 }
2885
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887                                                  int type)
2888 {
2889   lfi->type = type;
2890   lfi->packed = TRUE;
2891
2892   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2894 }
2895
2896 static int getFiletypeFromID(char *filetype_id)
2897 {
2898   char *filetype_id_lower;
2899   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2900   int i;
2901
2902   if (filetype_id == NULL)
2903     return LEVEL_FILE_TYPE_UNKNOWN;
2904
2905   filetype_id_lower = getStringToLower(filetype_id);
2906
2907   for (i = 0; filetype_id_list[i].id != NULL; i++)
2908   {
2909     char *id_lower = getStringToLower(filetype_id_list[i].id);
2910     
2911     if (strEqual(filetype_id_lower, id_lower))
2912       filetype = filetype_id_list[i].filetype;
2913
2914     free(id_lower);
2915
2916     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2917       break;
2918   }
2919
2920   free(filetype_id_lower);
2921
2922   return filetype;
2923 }
2924
2925 char *getLocalLevelTemplateFilename(void)
2926 {
2927   return getDefaultLevelFilename(-1);
2928 }
2929
2930 char *getGlobalLevelTemplateFilename(void)
2931 {
2932   // global variable "leveldir_current" must be modified in the loop below
2933   LevelDirTree *leveldir_current_last = leveldir_current;
2934   char *filename = NULL;
2935
2936   // check for template level in path from current to topmost tree node
2937
2938   while (leveldir_current != NULL)
2939   {
2940     filename = getDefaultLevelFilename(-1);
2941
2942     if (fileExists(filename))
2943       break;
2944
2945     leveldir_current = leveldir_current->node_parent;
2946   }
2947
2948   // restore global variable "leveldir_current" modified in above loop
2949   leveldir_current = leveldir_current_last;
2950
2951   return filename;
2952 }
2953
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2955 {
2956   int nr = lfi->nr;
2957
2958   // special case: level number is negative => check for level template file
2959   if (nr < 0)
2960   {
2961     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962                                          getSingleLevelBasename(-1));
2963
2964     // replace local level template filename with global template filename
2965     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2966
2967     // no fallback if template file not existing
2968     return;
2969   }
2970
2971   // special case: check for file name/pattern specified in "levelinfo.conf"
2972   if (leveldir_current->level_filename != NULL)
2973   {
2974     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2975
2976     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977                                          leveldir_current->level_filename, nr);
2978
2979     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2980
2981     if (fileExists(lfi->filename))
2982       return;
2983   }
2984   else if (leveldir_current->level_filetype != NULL)
2985   {
2986     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2987
2988     // check for specified native level file with standard file name
2989     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2991     if (fileExists(lfi->filename))
2992       return;
2993   }
2994
2995   // check for native Rocks'n'Diamonds level file
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2998   if (fileExists(lfi->filename))
2999     return;
3000
3001   // check for native Boulder Dash level file
3002   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003   if (fileExists(lfi->filename))
3004     return;
3005
3006   // check for Emerald Mine level file (V1)
3007   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008                                        'a' + (nr / 10) % 26, '0' + nr % 10);
3009   if (fileExists(lfi->filename))
3010     return;
3011   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012                                        'A' + (nr / 10) % 26, '0' + nr % 10);
3013   if (fileExists(lfi->filename))
3014     return;
3015
3016   // check for Emerald Mine level file (V2 to V5)
3017   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018   if (fileExists(lfi->filename))
3019     return;
3020
3021   // check for Emerald Mine level file (V6 / single mode)
3022   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023   if (fileExists(lfi->filename))
3024     return;
3025   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026   if (fileExists(lfi->filename))
3027     return;
3028
3029   // check for Emerald Mine level file (V6 / teamwork mode)
3030   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031   if (fileExists(lfi->filename))
3032     return;
3033   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034   if (fileExists(lfi->filename))
3035     return;
3036
3037   // check for various packed level file formats
3038   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039   if (fileExists(lfi->filename))
3040     return;
3041
3042   // no known level file found -- use default values (and fail later)
3043   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3045 }
3046
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3048 {
3049   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050     lfi->type = getFileTypeFromBasename(lfi->basename);
3051
3052   if (lfi->type == LEVEL_FILE_TYPE_RND)
3053     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3054 }
3055
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3057 {
3058   // always start with reliable default values
3059   setFileInfoToDefaults(level_file_info);
3060
3061   level_file_info->nr = nr;     // set requested level number
3062
3063   determineLevelFileInfo_Filename(level_file_info);
3064   determineLevelFileInfo_Filetype(level_file_info);
3065 }
3066
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068                               struct LevelFileInfo *lfi_to)
3069 {
3070   lfi_to->nr     = lfi_from->nr;
3071   lfi_to->type   = lfi_from->type;
3072   lfi_to->packed = lfi_from->packed;
3073
3074   setString(&lfi_to->basename, lfi_from->basename);
3075   setString(&lfi_to->filename, lfi_from->filename);
3076 }
3077
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3081
3082 int getMappedElement(int element)
3083 {
3084   // remap some (historic, now obsolete) elements
3085
3086   switch (element)
3087   {
3088     case EL_PLAYER_OBSOLETE:
3089       element = EL_PLAYER_1;
3090       break;
3091
3092     case EL_KEY_OBSOLETE:
3093       element = EL_KEY_1;
3094       break;
3095
3096     case EL_EM_KEY_1_FILE_OBSOLETE:
3097       element = EL_EM_KEY_1;
3098       break;
3099
3100     case EL_EM_KEY_2_FILE_OBSOLETE:
3101       element = EL_EM_KEY_2;
3102       break;
3103
3104     case EL_EM_KEY_3_FILE_OBSOLETE:
3105       element = EL_EM_KEY_3;
3106       break;
3107
3108     case EL_EM_KEY_4_FILE_OBSOLETE:
3109       element = EL_EM_KEY_4;
3110       break;
3111
3112     case EL_ENVELOPE_OBSOLETE:
3113       element = EL_ENVELOPE_1;
3114       break;
3115
3116     case EL_SP_EMPTY:
3117       element = EL_EMPTY;
3118       break;
3119
3120     default:
3121       if (element >= NUM_FILE_ELEMENTS)
3122       {
3123         Warn("invalid level element %d", element);
3124
3125         element = EL_UNKNOWN;
3126       }
3127       break;
3128   }
3129
3130   return element;
3131 }
3132
3133 static int getMappedElementByVersion(int element, int game_version)
3134 {
3135   // remap some elements due to certain game version
3136
3137   if (game_version <= VERSION_IDENT(2,2,0,0))
3138   {
3139     // map game font elements
3140     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3141                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3143                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3144   }
3145
3146   if (game_version < VERSION_IDENT(3,0,0,0))
3147   {
3148     // map Supaplex gravity tube elements
3149     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3150                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3152                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3153                element);
3154   }
3155
3156   return element;
3157 }
3158
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3160 {
3161   level->file_version = getFileVersion(file);
3162   level->game_version = getFileVersion(file);
3163
3164   return chunk_size;
3165 }
3166
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3168 {
3169   level->creation_date.year  = getFile16BitBE(file);
3170   level->creation_date.month = getFile8Bit(file);
3171   level->creation_date.day   = getFile8Bit(file);
3172
3173   level->creation_date.src   = DATE_SRC_LEVELFILE;
3174
3175   return chunk_size;
3176 }
3177
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3179 {
3180   int initial_player_stepsize;
3181   int initial_player_gravity;
3182   int i, x, y;
3183
3184   level->fieldx = getFile8Bit(file);
3185   level->fieldy = getFile8Bit(file);
3186
3187   level->time           = getFile16BitBE(file);
3188   level->gems_needed    = getFile16BitBE(file);
3189
3190   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191     level->name[i] = getFile8Bit(file);
3192   level->name[MAX_LEVEL_NAME_LEN] = 0;
3193
3194   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195     level->score[i] = getFile8Bit(file);
3196
3197   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199     for (y = 0; y < 3; y++)
3200       for (x = 0; x < 3; x++)
3201         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3202
3203   level->amoeba_speed           = getFile8Bit(file);
3204   level->time_magic_wall        = getFile8Bit(file);
3205   level->time_wheel             = getFile8Bit(file);
3206   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3207
3208   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3209                                    STEPSIZE_NORMAL);
3210
3211   for (i = 0; i < MAX_PLAYERS; i++)
3212     level->initial_player_stepsize[i] = initial_player_stepsize;
3213
3214   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215
3216   for (i = 0; i < MAX_PLAYERS; i++)
3217     level->initial_player_gravity[i] = initial_player_gravity;
3218
3219   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221
3222   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223
3224   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226   level->can_move_into_acid_bits = getFile32BitBE(file);
3227   level->dont_collide_with_bits = getFile8Bit(file);
3228
3229   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3231
3232   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3235
3236   level->game_engine_type       = getFile8Bit(file);
3237
3238   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3239
3240   return chunk_size;
3241 }
3242
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3244 {
3245   int i;
3246
3247   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248     level->name[i] = getFile8Bit(file);
3249   level->name[MAX_LEVEL_NAME_LEN] = 0;
3250
3251   return chunk_size;
3252 }
3253
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3255 {
3256   int i;
3257
3258   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259     level->author[i] = getFile8Bit(file);
3260   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3261
3262   return chunk_size;
3263 }
3264
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3266 {
3267   int x, y;
3268   int chunk_size_expected = level->fieldx * level->fieldy;
3269
3270   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271      stored with 16-bit encoding (and should be twice as big then).
3272      Even worse, playfield data was stored 16-bit when only yamyam content
3273      contained 16-bit elements and vice versa. */
3274
3275   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276     chunk_size_expected *= 2;
3277
3278   if (chunk_size_expected != chunk_size)
3279   {
3280     ReadUnusedBytesFromFile(file, chunk_size);
3281     return chunk_size_expected;
3282   }
3283
3284   for (y = 0; y < level->fieldy; y++)
3285     for (x = 0; x < level->fieldx; x++)
3286       level->field[x][y] =
3287         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3288                          getFile8Bit(file));
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i, x, y;
3295   int header_size = 4;
3296   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297   int chunk_size_expected = header_size + content_size;
3298
3299   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300      stored with 16-bit encoding (and should be twice as big then).
3301      Even worse, playfield data was stored 16-bit when only yamyam content
3302      contained 16-bit elements and vice versa. */
3303
3304   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305     chunk_size_expected += content_size;
3306
3307   if (chunk_size_expected != chunk_size)
3308   {
3309     ReadUnusedBytesFromFile(file, chunk_size);
3310     return chunk_size_expected;
3311   }
3312
3313   getFile8Bit(file);
3314   level->num_yamyam_contents = getFile8Bit(file);
3315   getFile8Bit(file);
3316   getFile8Bit(file);
3317
3318   // correct invalid number of content fields -- should never happen
3319   if (level->num_yamyam_contents < 1 ||
3320       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3322
3323   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324     for (y = 0; y < 3; y++)
3325       for (x = 0; x < 3; x++)
3326         level->yamyam_content[i].e[x][y] =
3327           getMappedElement(level->encoding_16bit_field ?
3328                            getFile16BitBE(file) : getFile8Bit(file));
3329   return chunk_size;
3330 }
3331
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3333 {
3334   int i, x, y;
3335   int element;
3336   int num_contents;
3337   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3338
3339   element = getMappedElement(getFile16BitBE(file));
3340   num_contents = getFile8Bit(file);
3341
3342   getFile8Bit(file);    // content x size (unused)
3343   getFile8Bit(file);    // content y size (unused)
3344
3345   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3346
3347   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348     for (y = 0; y < 3; y++)
3349       for (x = 0; x < 3; x++)
3350         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3351
3352   // correct invalid number of content fields -- should never happen
3353   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354     num_contents = STD_ELEMENT_CONTENTS;
3355
3356   if (element == EL_YAMYAM)
3357   {
3358     level->num_yamyam_contents = num_contents;
3359
3360     for (i = 0; i < num_contents; i++)
3361       for (y = 0; y < 3; y++)
3362         for (x = 0; x < 3; x++)
3363           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3364   }
3365   else if (element == EL_BD_AMOEBA)
3366   {
3367     level->amoeba_content = content_array[0][0][0];
3368   }
3369   else
3370   {
3371     Warn("cannot load content for element '%d'", element);
3372   }
3373
3374   return chunk_size;
3375 }
3376
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3378 {
3379   int i;
3380   int element;
3381   int envelope_nr;
3382   int envelope_len;
3383   int chunk_size_expected;
3384
3385   element = getMappedElement(getFile16BitBE(file));
3386   if (!IS_ENVELOPE(element))
3387     element = EL_ENVELOPE_1;
3388
3389   envelope_nr = element - EL_ENVELOPE_1;
3390
3391   envelope_len = getFile16BitBE(file);
3392
3393   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3395
3396   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3397
3398   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399   if (chunk_size_expected != chunk_size)
3400   {
3401     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402     return chunk_size_expected;
3403   }
3404
3405   for (i = 0; i < envelope_len; i++)
3406     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3407
3408   return chunk_size;
3409 }
3410
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3412 {
3413   int num_changed_custom_elements = getFile16BitBE(file);
3414   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3415   int i;
3416
3417   if (chunk_size_expected != chunk_size)
3418   {
3419     ReadUnusedBytesFromFile(file, chunk_size - 2);
3420     return chunk_size_expected;
3421   }
3422
3423   for (i = 0; i < num_changed_custom_elements; i++)
3424   {
3425     int element = getMappedElement(getFile16BitBE(file));
3426     int properties = getFile32BitBE(file);
3427
3428     if (IS_CUSTOM_ELEMENT(element))
3429       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3430     else
3431       Warn("invalid custom element number %d", element);
3432
3433     // older game versions that wrote level files with CUS1 chunks used
3434     // different default push delay values (not yet stored in level file)
3435     element_info[element].push_delay_fixed = 2;
3436     element_info[element].push_delay_random = 8;
3437   }
3438
3439   level->file_has_custom_elements = TRUE;
3440
3441   return chunk_size;
3442 }
3443
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3445 {
3446   int num_changed_custom_elements = getFile16BitBE(file);
3447   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3448   int i;
3449
3450   if (chunk_size_expected != chunk_size)
3451   {
3452     ReadUnusedBytesFromFile(file, chunk_size - 2);
3453     return chunk_size_expected;
3454   }
3455
3456   for (i = 0; i < num_changed_custom_elements; i++)
3457   {
3458     int element = getMappedElement(getFile16BitBE(file));
3459     int custom_target_element = getMappedElement(getFile16BitBE(file));
3460
3461     if (IS_CUSTOM_ELEMENT(element))
3462       element_info[element].change->target_element = custom_target_element;
3463     else
3464       Warn("invalid custom element number %d", element);
3465   }
3466
3467   level->file_has_custom_elements = TRUE;
3468
3469   return chunk_size;
3470 }
3471
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3473 {
3474   int num_changed_custom_elements = getFile16BitBE(file);
3475   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3476   int i, j, x, y;
3477
3478   if (chunk_size_expected != chunk_size)
3479   {
3480     ReadUnusedBytesFromFile(file, chunk_size - 2);
3481     return chunk_size_expected;
3482   }
3483
3484   for (i = 0; i < num_changed_custom_elements; i++)
3485   {
3486     int element = getMappedElement(getFile16BitBE(file));
3487     struct ElementInfo *ei = &element_info[element];
3488     unsigned int event_bits;
3489
3490     if (!IS_CUSTOM_ELEMENT(element))
3491     {
3492       Warn("invalid custom element number %d", element);
3493
3494       element = EL_INTERNAL_DUMMY;
3495     }
3496
3497     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498       ei->description[j] = getFile8Bit(file);
3499     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3500
3501     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3502
3503     // some free bytes for future properties and padding
3504     ReadUnusedBytesFromFile(file, 7);
3505
3506     ei->use_gfx_element = getFile8Bit(file);
3507     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3508
3509     ei->collect_score_initial = getFile8Bit(file);
3510     ei->collect_count_initial = getFile8Bit(file);
3511
3512     ei->push_delay_fixed = getFile16BitBE(file);
3513     ei->push_delay_random = getFile16BitBE(file);
3514     ei->move_delay_fixed = getFile16BitBE(file);
3515     ei->move_delay_random = getFile16BitBE(file);
3516
3517     ei->move_pattern = getFile16BitBE(file);
3518     ei->move_direction_initial = getFile8Bit(file);
3519     ei->move_stepsize = getFile8Bit(file);
3520
3521     for (y = 0; y < 3; y++)
3522       for (x = 0; x < 3; x++)
3523         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3524
3525     // bits 0 - 31 of "has_event[]"
3526     event_bits = getFile32BitBE(file);
3527     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528       if (event_bits & (1u << j))
3529         ei->change->has_event[j] = TRUE;
3530
3531     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3532
3533     ei->change->delay_fixed = getFile16BitBE(file);
3534     ei->change->delay_random = getFile16BitBE(file);
3535     ei->change->delay_frames = getFile16BitBE(file);
3536
3537     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3538
3539     ei->change->explode = getFile8Bit(file);
3540     ei->change->use_target_content = getFile8Bit(file);
3541     ei->change->only_if_complete = getFile8Bit(file);
3542     ei->change->use_random_replace = getFile8Bit(file);
3543
3544     ei->change->random_percentage = getFile8Bit(file);
3545     ei->change->replace_when = getFile8Bit(file);
3546
3547     for (y = 0; y < 3; y++)
3548       for (x = 0; x < 3; x++)
3549         ei->change->target_content.e[x][y] =
3550           getMappedElement(getFile16BitBE(file));
3551
3552     ei->slippery_type = getFile8Bit(file);
3553
3554     // some free bytes for future properties and padding
3555     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3556
3557     // mark that this custom element has been modified
3558     ei->modified_settings = TRUE;
3559   }
3560
3561   level->file_has_custom_elements = TRUE;
3562
3563   return chunk_size;
3564 }
3565
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3567 {
3568   struct ElementInfo *ei;
3569   int chunk_size_expected;
3570   int element;
3571   int i, j, x, y;
3572
3573   // ---------- custom element base property values (96 bytes) ----------------
3574
3575   element = getMappedElement(getFile16BitBE(file));
3576
3577   if (!IS_CUSTOM_ELEMENT(element))
3578   {
3579     Warn("invalid custom element number %d", element);
3580
3581     ReadUnusedBytesFromFile(file, chunk_size - 2);
3582
3583     return chunk_size;
3584   }
3585
3586   ei = &element_info[element];
3587
3588   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589     ei->description[i] = getFile8Bit(file);
3590   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3591
3592   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3593
3594   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3595
3596   ei->num_change_pages = getFile8Bit(file);
3597
3598   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599   if (chunk_size_expected != chunk_size)
3600   {
3601     ReadUnusedBytesFromFile(file, chunk_size - 43);
3602     return chunk_size_expected;
3603   }
3604
3605   ei->ce_value_fixed_initial = getFile16BitBE(file);
3606   ei->ce_value_random_initial = getFile16BitBE(file);
3607   ei->use_last_ce_value = getFile8Bit(file);
3608
3609   ei->use_gfx_element = getFile8Bit(file);
3610   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3611
3612   ei->collect_score_initial = getFile8Bit(file);
3613   ei->collect_count_initial = getFile8Bit(file);
3614
3615   ei->drop_delay_fixed = getFile8Bit(file);
3616   ei->push_delay_fixed = getFile8Bit(file);
3617   ei->drop_delay_random = getFile8Bit(file);
3618   ei->push_delay_random = getFile8Bit(file);
3619   ei->move_delay_fixed = getFile16BitBE(file);
3620   ei->move_delay_random = getFile16BitBE(file);
3621
3622   // bits 0 - 15 of "move_pattern" ...
3623   ei->move_pattern = getFile16BitBE(file);
3624   ei->move_direction_initial = getFile8Bit(file);
3625   ei->move_stepsize = getFile8Bit(file);
3626
3627   ei->slippery_type = getFile8Bit(file);
3628
3629   for (y = 0; y < 3; y++)
3630     for (x = 0; x < 3; x++)
3631       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3632
3633   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635   ei->move_leave_type = getFile8Bit(file);
3636
3637   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638   ei->move_pattern |= (getFile16BitBE(file) << 16);
3639
3640   ei->access_direction = getFile8Bit(file);
3641
3642   ei->explosion_delay = getFile8Bit(file);
3643   ei->ignition_delay = getFile8Bit(file);
3644   ei->explosion_type = getFile8Bit(file);
3645
3646   // some free bytes for future custom property values and padding
3647   ReadUnusedBytesFromFile(file, 1);
3648
3649   // ---------- change page property values (48 bytes) ------------------------
3650
3651   setElementChangePages(ei, ei->num_change_pages);
3652
3653   for (i = 0; i < ei->num_change_pages; i++)
3654   {
3655     struct ElementChangeInfo *change = &ei->change_page[i];
3656     unsigned int event_bits;
3657
3658     // always start with reliable default values
3659     setElementChangeInfoToDefaults(change);
3660
3661     // bits 0 - 31 of "has_event[]" ...
3662     event_bits = getFile32BitBE(file);
3663     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664       if (event_bits & (1u << j))
3665         change->has_event[j] = TRUE;
3666
3667     change->target_element = getMappedElement(getFile16BitBE(file));
3668
3669     change->delay_fixed = getFile16BitBE(file);
3670     change->delay_random = getFile16BitBE(file);
3671     change->delay_frames = getFile16BitBE(file);
3672
3673     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3674
3675     change->explode = getFile8Bit(file);
3676     change->use_target_content = getFile8Bit(file);
3677     change->only_if_complete = getFile8Bit(file);
3678     change->use_random_replace = getFile8Bit(file);
3679
3680     change->random_percentage = getFile8Bit(file);
3681     change->replace_when = getFile8Bit(file);
3682
3683     for (y = 0; y < 3; y++)
3684       for (x = 0; x < 3; x++)
3685         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3686
3687     change->can_change = getFile8Bit(file);
3688
3689     change->trigger_side = getFile8Bit(file);
3690
3691     change->trigger_player = getFile8Bit(file);
3692     change->trigger_page = getFile8Bit(file);
3693
3694     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695                             CH_PAGE_ANY : (1 << change->trigger_page));
3696
3697     change->has_action = getFile8Bit(file);
3698     change->action_type = getFile8Bit(file);
3699     change->action_mode = getFile8Bit(file);
3700     change->action_arg = getFile16BitBE(file);
3701
3702     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703     event_bits = getFile8Bit(file);
3704     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705       if (event_bits & (1u << (j - 32)))
3706         change->has_event[j] = TRUE;
3707   }
3708
3709   // mark this custom element as modified
3710   ei->modified_settings = TRUE;
3711
3712   level->file_has_custom_elements = TRUE;
3713
3714   return chunk_size;
3715 }
3716
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3718 {
3719   struct ElementInfo *ei;
3720   struct ElementGroupInfo *group;
3721   int element;
3722   int i;
3723
3724   element = getMappedElement(getFile16BitBE(file));
3725
3726   if (!IS_GROUP_ELEMENT(element))
3727   {
3728     Warn("invalid group element number %d", element);
3729
3730     ReadUnusedBytesFromFile(file, chunk_size - 2);
3731
3732     return chunk_size;
3733   }
3734
3735   ei = &element_info[element];
3736
3737   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738     ei->description[i] = getFile8Bit(file);
3739   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3740
3741   group = element_info[element].group;
3742
3743   group->num_elements = getFile8Bit(file);
3744
3745   ei->use_gfx_element = getFile8Bit(file);
3746   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3747
3748   group->choice_mode = getFile8Bit(file);
3749
3750   // some free bytes for future values and padding
3751   ReadUnusedBytesFromFile(file, 3);
3752
3753   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754     group->element[i] = getMappedElement(getFile16BitBE(file));
3755
3756   // mark this group element as modified
3757   element_info[element].modified_settings = TRUE;
3758
3759   level->file_has_custom_elements = TRUE;
3760
3761   return chunk_size;
3762 }
3763
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765                                 int element, int real_element)
3766 {
3767   int micro_chunk_size = 0;
3768   int conf_type = getFile8Bit(file);
3769   int byte_mask = conf_type & CONF_MASK_BYTES;
3770   boolean element_found = FALSE;
3771   int i;
3772
3773   micro_chunk_size += 1;
3774
3775   if (byte_mask == CONF_MASK_MULTI_BYTES)
3776   {
3777     int num_bytes = getFile16BitBE(file);
3778     byte *buffer = checked_malloc(num_bytes);
3779
3780     ReadBytesFromFile(file, buffer, num_bytes);
3781
3782     for (i = 0; conf[i].data_type != -1; i++)
3783     {
3784       if (conf[i].element == element &&
3785           conf[i].conf_type == conf_type)
3786       {
3787         int data_type = conf[i].data_type;
3788         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789         int max_num_entities = conf[i].max_num_entities;
3790
3791         if (num_entities > max_num_entities)
3792         {
3793           Warn("truncating number of entities for element %d from %d to %d",
3794                element, num_entities, max_num_entities);
3795
3796           num_entities = max_num_entities;
3797         }
3798
3799         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800                                   data_type == TYPE_CONTENT_LIST))
3801         {
3802           // for element and content lists, zero entities are not allowed
3803           Warn("found empty list of entities for element %d", element);
3804
3805           // do not set "num_entities" here to prevent reading behind buffer
3806
3807           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3808         }
3809         else
3810         {
3811           *(int *)(conf[i].num_entities) = num_entities;
3812         }
3813
3814         element_found = TRUE;
3815
3816         if (data_type == TYPE_STRING)
3817         {
3818           char *string = (char *)(conf[i].value);
3819           int j;
3820
3821           for (j = 0; j < max_num_entities; j++)
3822             string[j] = (j < num_entities ? buffer[j] : '\0');
3823         }
3824         else if (data_type == TYPE_ELEMENT_LIST)
3825         {
3826           int *element_array = (int *)(conf[i].value);
3827           int j;
3828
3829           for (j = 0; j < num_entities; j++)
3830             element_array[j] =
3831               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3832         }
3833         else if (data_type == TYPE_CONTENT_LIST)
3834         {
3835           struct Content *content= (struct Content *)(conf[i].value);
3836           int c, x, y;
3837
3838           for (c = 0; c < num_entities; c++)
3839             for (y = 0; y < 3; y++)
3840               for (x = 0; x < 3; x++)
3841                 content[c].e[x][y] =
3842                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3843         }
3844         else
3845           element_found = FALSE;
3846
3847         break;
3848       }
3849     }
3850
3851     checked_free(buffer);
3852
3853     micro_chunk_size += 2 + num_bytes;
3854   }
3855   else          // constant size configuration data (1, 2 or 4 bytes)
3856   {
3857     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3858                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3860
3861     for (i = 0; conf[i].data_type != -1; i++)
3862     {
3863       if (conf[i].element == element &&
3864           conf[i].conf_type == conf_type)
3865       {
3866         int data_type = conf[i].data_type;
3867
3868         if (data_type == TYPE_ELEMENT)
3869           value = getMappedElement(value);
3870
3871         if (data_type == TYPE_BOOLEAN)
3872           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3873         else
3874           *(int *)    (conf[i].value) = value;
3875
3876         element_found = TRUE;
3877
3878         break;
3879       }
3880     }
3881
3882     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3883   }
3884
3885   if (!element_found)
3886   {
3887     char *error_conf_chunk_bytes =
3888       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892     int error_element = real_element;
3893
3894     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895          error_conf_chunk_bytes, error_conf_chunk_token,
3896          error_element, EL_NAME(error_element));
3897   }
3898
3899   return micro_chunk_size;
3900 }
3901
3902 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int real_chunk_size = 0;
3905
3906   li = *level;          // copy level data into temporary buffer
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3911
3912     if (real_chunk_size >= chunk_size)
3913       break;
3914   }
3915
3916   *level = li;          // copy temporary buffer back to level data
3917
3918   return real_chunk_size;
3919 }
3920
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3922 {
3923   int real_chunk_size = 0;
3924
3925   li = *level;          // copy level data into temporary buffer
3926
3927   while (!checkEndOfFile(file))
3928   {
3929     int element = getMappedElement(getFile16BitBE(file));
3930
3931     real_chunk_size += 2;
3932     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3933                                             element, element);
3934     if (real_chunk_size >= chunk_size)
3935       break;
3936   }
3937
3938   *level = li;          // copy temporary buffer back to level data
3939
3940   return real_chunk_size;
3941 }
3942
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3944 {
3945   int real_chunk_size = 0;
3946
3947   li = *level;          // copy level data into temporary buffer
3948
3949   while (!checkEndOfFile(file))
3950   {
3951     int element = getMappedElement(getFile16BitBE(file));
3952
3953     real_chunk_size += 2;
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3955                                             element, element);
3956     if (real_chunk_size >= chunk_size)
3957       break;
3958   }
3959
3960   *level = li;          // copy temporary buffer back to level data
3961
3962   return real_chunk_size;
3963 }
3964
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3966 {
3967   int element = getMappedElement(getFile16BitBE(file));
3968   int envelope_nr = element - EL_ENVELOPE_1;
3969   int real_chunk_size = 2;
3970
3971   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3972
3973   while (!checkEndOfFile(file))
3974   {
3975     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3976                                             -1, element);
3977
3978     if (real_chunk_size >= chunk_size)
3979       break;
3980   }
3981
3982   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3983
3984   return real_chunk_size;
3985 }
3986
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3988 {
3989   int element = getMappedElement(getFile16BitBE(file));
3990   int real_chunk_size = 2;
3991   struct ElementInfo *ei = &element_info[element];
3992   int i;
3993
3994   xx_ei = *ei;          // copy element data into temporary buffer
3995
3996   xx_ei.num_change_pages = -1;
3997
3998   while (!checkEndOfFile(file))
3999   {
4000     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4001                                             -1, element);
4002     if (xx_ei.num_change_pages != -1)
4003       break;
4004
4005     if (real_chunk_size >= chunk_size)
4006       break;
4007   }
4008
4009   *ei = xx_ei;
4010
4011   if (ei->num_change_pages == -1)
4012   {
4013     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4014          EL_NAME(element));
4015
4016     ei->num_change_pages = 1;
4017
4018     setElementChangePages(ei, 1);
4019     setElementChangeInfoToDefaults(ei->change);
4020
4021     return real_chunk_size;
4022   }
4023
4024   // initialize number of change pages stored for this custom element
4025   setElementChangePages(ei, ei->num_change_pages);
4026   for (i = 0; i < ei->num_change_pages; i++)
4027     setElementChangeInfoToDefaults(&ei->change_page[i]);
4028
4029   // start with reading properties for the first change page
4030   xx_current_change_page = 0;
4031
4032   while (!checkEndOfFile(file))
4033   {
4034     // level file might contain invalid change page number
4035     if (xx_current_change_page >= ei->num_change_pages)
4036       break;
4037
4038     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4039
4040     xx_change = *change;        // copy change data into temporary buffer
4041
4042     resetEventBits();           // reset bits; change page might have changed
4043
4044     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4045                                             -1, element);
4046
4047     *change = xx_change;
4048
4049     setEventFlagsFromEventBits(change);
4050
4051     if (real_chunk_size >= chunk_size)
4052       break;
4053   }
4054
4055   level->file_has_custom_elements = TRUE;
4056
4057   return real_chunk_size;
4058 }
4059
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4061 {
4062   int element = getMappedElement(getFile16BitBE(file));
4063   int real_chunk_size = 2;
4064   struct ElementInfo *ei = &element_info[element];
4065   struct ElementGroupInfo *group = ei->group;
4066
4067   if (group == NULL)
4068     return -1;
4069
4070   xx_ei = *ei;          // copy element data into temporary buffer
4071   xx_group = *group;    // copy group data into temporary buffer
4072
4073   while (!checkEndOfFile(file))
4074   {
4075     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4076                                             -1, element);
4077
4078     if (real_chunk_size >= chunk_size)
4079       break;
4080   }
4081
4082   *ei = xx_ei;
4083   *group = xx_group;
4084
4085   level->file_has_custom_elements = TRUE;
4086
4087   return real_chunk_size;
4088 }
4089
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4091 {
4092   int element = getMappedElement(getFile16BitBE(file));
4093   int real_chunk_size = 2;
4094   struct ElementInfo *ei = &element_info[element];
4095
4096   xx_ei = *ei;          // copy element data into temporary buffer
4097
4098   while (!checkEndOfFile(file))
4099   {
4100     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4101                                             -1, element);
4102
4103     if (real_chunk_size >= chunk_size)
4104       break;
4105   }
4106
4107   *ei = xx_ei;
4108
4109   level->file_has_custom_elements = TRUE;
4110
4111   return real_chunk_size;
4112 }
4113
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115                                       struct LevelFileInfo *level_file_info,
4116                                       boolean level_info_only)
4117 {
4118   char *filename = level_file_info->filename;
4119   char cookie[MAX_LINE_LEN];
4120   char chunk_name[CHUNK_ID_LEN + 1];
4121   int chunk_size;
4122   File *file;
4123
4124   if (!(file = openFile(filename, MODE_READ)))
4125   {
4126     level->no_valid_file = TRUE;
4127     level->no_level_file = TRUE;
4128
4129     if (level_info_only)
4130       return;
4131
4132     Warn("cannot read level '%s' -- using empty level", filename);
4133
4134     if (!setup.editor.use_template_for_new_levels)
4135       return;
4136
4137     // if level file not found, try to initialize level data from template
4138     filename = getGlobalLevelTemplateFilename();
4139
4140     if (!(file = openFile(filename, MODE_READ)))
4141       return;
4142
4143     // default: for empty levels, use level template for custom elements
4144     level->use_custom_template = TRUE;
4145
4146     level->no_valid_file = FALSE;
4147   }
4148
4149   getFileChunkBE(file, chunk_name, NULL);
4150   if (strEqual(chunk_name, "RND1"))
4151   {
4152     getFile32BitBE(file);               // not used
4153
4154     getFileChunkBE(file, chunk_name, NULL);
4155     if (!strEqual(chunk_name, "CAVE"))
4156     {
4157       level->no_valid_file = TRUE;
4158
4159       Warn("unknown format of level file '%s'", filename);
4160
4161       closeFile(file);
4162
4163       return;
4164     }
4165   }
4166   else  // check for pre-2.0 file format with cookie string
4167   {
4168     strcpy(cookie, chunk_name);
4169     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4170       cookie[4] = '\0';
4171     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172       cookie[strlen(cookie) - 1] = '\0';
4173
4174     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4175     {
4176       level->no_valid_file = TRUE;
4177
4178       Warn("unknown format of level file '%s'", filename);
4179
4180       closeFile(file);
4181
4182       return;
4183     }
4184
4185     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4186     {
4187       level->no_valid_file = TRUE;
4188
4189       Warn("unsupported version of level file '%s'", filename);
4190
4191       closeFile(file);
4192
4193       return;
4194     }
4195
4196     // pre-2.0 level files have no game version, so use file version here
4197     level->game_version = level->file_version;
4198   }
4199
4200   if (level->file_version < FILE_VERSION_1_2)
4201   {
4202     // level files from versions before 1.2.0 without chunk structure
4203     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4204     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4205   }
4206   else
4207   {
4208     static struct
4209     {
4210       char *name;
4211       int size;
4212       int (*loader)(File *, int, struct LevelInfo *);
4213     }
4214     chunk_info[] =
4215     {
4216       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4217       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4218       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4219       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4220       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4221       { "INFO", -1,                     LoadLevel_INFO },
4222       { "BODY", -1,                     LoadLevel_BODY },
4223       { "CONT", -1,                     LoadLevel_CONT },
4224       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4225       { "CNT3", -1,                     LoadLevel_CNT3 },
4226       { "CUS1", -1,                     LoadLevel_CUS1 },
4227       { "CUS2", -1,                     LoadLevel_CUS2 },
4228       { "CUS3", -1,                     LoadLevel_CUS3 },
4229       { "CUS4", -1,                     LoadLevel_CUS4 },
4230       { "GRP1", -1,                     LoadLevel_GRP1 },
4231       { "CONF", -1,                     LoadLevel_CONF },
4232       { "ELEM", -1,                     LoadLevel_ELEM },
4233       { "NOTE", -1,                     LoadLevel_NOTE },
4234       { "CUSX", -1,                     LoadLevel_CUSX },
4235       { "GRPX", -1,                     LoadLevel_GRPX },
4236       { "EMPX", -1,                     LoadLevel_EMPX },
4237
4238       {  NULL,  0,                      NULL }
4239     };
4240
4241     while (getFileChunkBE(file, chunk_name, &chunk_size))
4242     {
4243       int i = 0;
4244
4245       while (chunk_info[i].name != NULL &&
4246              !strEqual(chunk_name, chunk_info[i].name))
4247         i++;
4248
4249       if (chunk_info[i].name == NULL)
4250       {
4251         Warn("unknown chunk '%s' in level file '%s'",
4252              chunk_name, filename);
4253
4254         ReadUnusedBytesFromFile(file, chunk_size);
4255       }
4256       else if (chunk_info[i].size != -1 &&
4257                chunk_info[i].size != chunk_size)
4258       {
4259         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260              chunk_size, chunk_name, filename);
4261
4262         ReadUnusedBytesFromFile(file, chunk_size);
4263       }
4264       else
4265       {
4266         // call function to load this level chunk
4267         int chunk_size_expected =
4268           (chunk_info[i].loader)(file, chunk_size, level);
4269
4270         if (chunk_size_expected < 0)
4271         {
4272           Warn("error reading chunk '%s' in level file '%s'",
4273                chunk_name, filename);
4274
4275           break;
4276         }
4277
4278         // the size of some chunks cannot be checked before reading other
4279         // chunks first (like "HEAD" and "BODY") that contain some header
4280         // information, so check them here
4281         if (chunk_size_expected != chunk_size)
4282         {
4283           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284                chunk_size, chunk_name, filename);
4285
4286           break;
4287         }
4288       }
4289     }
4290   }
4291
4292   closeFile(file);
4293 }
4294
4295
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4299
4300 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4302
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4304 {
4305   struct LevelInfo_BD *level_bd = level->native_bd_level;
4306   GdCave *cave = NULL;  // will be changed below
4307   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4309   int x, y;
4310
4311   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4312
4313   // cave and map newly allocated when set to defaults above
4314   cave = level_bd->cave;
4315
4316   // level type
4317   cave->intermission                    = level->bd_intermission;
4318
4319   // level settings
4320   cave->level_time[0]                   = level->time;
4321   cave->level_diamonds[0]               = level->gems_needed;
4322
4323   // game timing
4324   cave->scheduling                      = level->bd_scheduling_type;
4325   cave->pal_timing                      = level->bd_pal_timing;
4326   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4327   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4328   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4329   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4330
4331   // scores
4332   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4333   cave->diamond_value                   = level->score[SC_EMERALD];
4334   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4335
4336   // compatibility settings
4337   cave->lineshift                       = level->bd_line_shifting_borders;
4338   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4339   cave->short_explosions                = level->bd_short_explosions;
4340
4341   // player properties
4342   cave->diagonal_movements              = level->bd_diagonal_movements;
4343   cave->active_is_first_found           = level->bd_topmost_player_active;
4344   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4345   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4346   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4348
4349   // element properties
4350   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4351   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4352   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4353   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4354   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4355   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4356   cave->level_magic_wall_time[0]        = level->bd_magic_wall_time;
4357   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4358   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4359   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4360   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4361
4362   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4369
4370   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4371   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4373   cave->level_amoeba_threshold[0]       = level->bd_amoeba_1_threshold_too_big;
4374   cave->level_amoeba_time[0]            = level->bd_amoeba_1_slow_growth_time;
4375   cave->amoeba_growth_prob              = level->bd_amoeba_1_slow_growth_rate * 10000;
4376   cave->amoeba_fast_growth_prob         = level->bd_amoeba_1_fast_growth_rate * 10000;
4377   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4378   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4379   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4380   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4381
4382   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4388
4389   cave->slime_predictable               = level->bd_slime_is_predictable;
4390   cave->slime_correct_random            = level->bd_slime_correct_random;
4391   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4392   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4394   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4395   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4401
4402   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4404   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4405
4406   cave->biter_delay_frame               = level->bd_biter_move_delay;
4407   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4408
4409   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4410
4411   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4412
4413   cave->replicators_active              = level->bd_replicators_active;
4414   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4415
4416   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4417   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4418
4419   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4420
4421   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4422
4423   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4424   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4425   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4426
4427   cave->infinite_rockets                = level->bd_infinite_rockets;
4428
4429   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4430   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4431
4432   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4434
4435   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4436   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4438
4439   cave->gravity                         = level->bd_gravity_direction;
4440   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4441   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4442   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4443
4444   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4448
4449   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4455
4456   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4460
4461   cave->colorb                          = level->bd_color_b;
4462   cave->color0                          = level->bd_color_0;
4463   cave->color1                          = level->bd_color_1;
4464   cave->color2                          = level->bd_color_2;
4465   cave->color3                          = level->bd_color_3;
4466   cave->color4                          = level->bd_color_4;
4467   cave->color5                          = level->bd_color_5;
4468
4469   // level name
4470   strncpy(cave->name, level->name, sizeof(GdString));
4471   cave->name[sizeof(GdString) - 1] = '\0';
4472
4473   // playfield elements
4474   for (x = 0; x < cave->w; x++)
4475     for (y = 0; y < cave->h; y++)
4476       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4477 }
4478
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4480 {
4481   struct LevelInfo_BD *level_bd = level->native_bd_level;
4482   GdCave *cave = level_bd->cave;
4483   int bd_level_nr = level_bd->level_nr;
4484   int x, y;
4485
4486   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4488
4489   // level type
4490   level->bd_intermission                = cave->intermission;
4491
4492   // level settings
4493   level->time                           = cave->level_time[bd_level_nr];
4494   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4495
4496   // game timing
4497   level->bd_scheduling_type             = cave->scheduling;
4498   level->bd_pal_timing                  = cave->pal_timing;
4499   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4500   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4501   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4502   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4503
4504   // scores
4505   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4506   level->score[SC_EMERALD]              = cave->diamond_value;
4507   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4508
4509   // compatibility settings
4510   level->bd_line_shifting_borders       = cave->lineshift;
4511   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4512   level->bd_short_explosions            = cave->short_explosions;
4513
4514   // player properties
4515   level->bd_diagonal_movements          = cave->diagonal_movements;
4516   level->bd_topmost_player_active       = cave->active_is_first_found;
4517   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4518   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4519   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4520   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4521
4522   // element properties
4523   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4524   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4525   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4526   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4527   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4528   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4529   level->bd_magic_wall_time             = cave->level_magic_wall_time[bd_level_nr];
4530   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4531   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4532   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4533   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4534
4535   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4537   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4539   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4542
4543   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4544   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4545   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4546   level->bd_amoeba_1_threshold_too_big  = cave->level_amoeba_threshold[bd_level_nr];
4547   level->bd_amoeba_1_slow_growth_time   = cave->level_amoeba_time[bd_level_nr];
4548   level->bd_amoeba_1_slow_growth_rate   = cave->amoeba_growth_prob      / 10000;
4549   level->bd_amoeba_1_fast_growth_rate   = cave->amoeba_fast_growth_prob / 10000;
4550   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4551   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4552   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4553   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4554
4555   level->bd_amoeba_1_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556   level->bd_amoeba_1_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4561
4562   level->bd_slime_is_predictable        = cave->slime_predictable;
4563   level->bd_slime_correct_random        = cave->slime_correct_random;
4564   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4565   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4567   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4568   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4569   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4571   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4573   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4574
4575   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4576   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4577   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4578
4579   level->bd_biter_move_delay            = cave->biter_delay_frame;
4580   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4581
4582   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4583
4584   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4585
4586   level->bd_replicators_active          = cave->replicators_active;
4587   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4588
4589   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4590   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4591
4592   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4593
4594   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4595
4596   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4597   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4598   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4599
4600   level->bd_infinite_rockets            = cave->infinite_rockets;
4601
4602   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4604
4605   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4607
4608   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4609   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4610   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4611
4612   level->bd_gravity_direction           = cave->gravity;
4613   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4614   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4615   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4616
4617   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4621
4622   level->bd_firefly_1_explodes_to       = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624   level->bd_butterfly_1_explodes_to     = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4628
4629   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4633
4634   level->bd_color_b                     = cave->colorb;
4635   level->bd_color_0                     = cave->color0;
4636   level->bd_color_1                     = cave->color1;
4637   level->bd_color_2                     = cave->color2;
4638   level->bd_color_3                     = cave->color3;
4639   level->bd_color_4                     = cave->color4;
4640   level->bd_color_5                     = cave->color5;
4641
4642   // set default color type and colors for BD style level colors
4643   SetDefaultLevelColorType_BD();
4644   SetDefaultLevelColors_BD();
4645
4646   // level name
4647   char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4648   char *cave_name_final = (gd_caveset_has_levels() ?
4649                            getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4650                            getStringCopy(cave_name_latin1));
4651
4652   strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4653   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4654
4655   // playfield elements
4656   for (x = 0; x < level->fieldx; x++)
4657     for (y = 0; y < level->fieldy; y++)
4658       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4659
4660   checked_free(cave_name_latin1);
4661   checked_free(cave_name_final);
4662 }
4663
4664 static void setTapeInfoToDefaults(void);
4665
4666 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4667 {
4668   struct LevelInfo_BD *level_bd = level->native_bd_level;
4669   GdCave *cave = level_bd->cave;
4670   GdReplay *replay = level_bd->replay;
4671   int i;
4672
4673   if (replay == NULL)
4674     return;
4675
4676   // always start with reliable default values
4677   setTapeInfoToDefaults();
4678
4679   tape.level_nr = level_nr;             // (currently not used)
4680   tape.random_seed = replay->seed;
4681
4682   TapeSetDateFromIsoDateString(replay->date);
4683
4684   tape.counter = 0;
4685   tape.pos[tape.counter].delay = 0;
4686
4687   tape.bd_replay = TRUE;
4688
4689   // all time calculations only used to display approximate tape time
4690   int cave_speed = cave->speed;
4691   int milliseconds_game = 0;
4692   int milliseconds_elapsed = 20;
4693
4694   for (i = 0; i < replay->movements->len; i++)
4695   {
4696     int replay_action = replay->movements->data[i];
4697     int tape_action = map_action_BD_to_RND(replay_action);
4698     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4699     boolean success = 0;
4700
4701     while (1)
4702     {
4703       success = TapeAddAction(action);
4704
4705       milliseconds_game += milliseconds_elapsed;
4706
4707       if (milliseconds_game >= cave_speed)
4708       {
4709         milliseconds_game -= cave_speed;
4710
4711         break;
4712       }
4713     }
4714
4715     tape.counter++;
4716     tape.pos[tape.counter].delay = 0;
4717     tape.pos[tape.counter].action[0] = 0;
4718
4719     if (!success)
4720     {
4721       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4722
4723       break;
4724     }
4725   }
4726
4727   TapeHaltRecording();
4728
4729   if (!replay->success)
4730     Warn("BD replay is marked as not successful");
4731 }
4732
4733
4734 // ----------------------------------------------------------------------------
4735 // functions for loading EM level
4736 // ----------------------------------------------------------------------------
4737
4738 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4739 {
4740   static int ball_xy[8][2] =
4741   {
4742     { 0, 0 },
4743     { 1, 0 },
4744     { 2, 0 },
4745     { 0, 1 },
4746     { 2, 1 },
4747     { 0, 2 },
4748     { 1, 2 },
4749     { 2, 2 },
4750   };
4751   struct LevelInfo_EM *level_em = level->native_em_level;
4752   struct CAVE *cav = level_em->cav;
4753   int i, j, x, y;
4754
4755   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4756   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4757
4758   cav->time_seconds     = level->time;
4759   cav->gems_needed      = level->gems_needed;
4760
4761   cav->emerald_score    = level->score[SC_EMERALD];
4762   cav->diamond_score    = level->score[SC_DIAMOND];
4763   cav->alien_score      = level->score[SC_ROBOT];
4764   cav->tank_score       = level->score[SC_SPACESHIP];
4765   cav->bug_score        = level->score[SC_BUG];
4766   cav->eater_score      = level->score[SC_YAMYAM];
4767   cav->nut_score        = level->score[SC_NUT];
4768   cav->dynamite_score   = level->score[SC_DYNAMITE];
4769   cav->key_score        = level->score[SC_KEY];
4770   cav->exit_score       = level->score[SC_TIME_BONUS];
4771
4772   cav->num_eater_arrays = level->num_yamyam_contents;
4773
4774   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4775     for (y = 0; y < 3; y++)
4776       for (x = 0; x < 3; x++)
4777         cav->eater_array[i][y * 3 + x] =
4778           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4779
4780   cav->amoeba_time              = level->amoeba_speed;
4781   cav->wonderwall_time          = level->time_magic_wall;
4782   cav->wheel_time               = level->time_wheel;
4783
4784   cav->android_move_time        = level->android_move_time;
4785   cav->android_clone_time       = level->android_clone_time;
4786   cav->ball_random              = level->ball_random;
4787   cav->ball_active              = level->ball_active_initial;
4788   cav->ball_time                = level->ball_time;
4789   cav->num_ball_arrays          = level->num_ball_contents;
4790
4791   cav->lenses_score             = level->lenses_score;
4792   cav->magnify_score            = level->magnify_score;
4793   cav->slurp_score              = level->slurp_score;
4794
4795   cav->lenses_time              = level->lenses_time;
4796   cav->magnify_time             = level->magnify_time;
4797
4798   cav->wind_time = 9999;
4799   cav->wind_direction =
4800     map_direction_RND_to_EM(level->wind_direction_initial);
4801
4802   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4803     for (j = 0; j < 8; j++)
4804       cav->ball_array[i][j] =
4805         map_element_RND_to_EM_cave(level->ball_content[i].
4806                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4807
4808   map_android_clone_elements_RND_to_EM(level);
4809
4810   // first fill the complete playfield with the empty space element
4811   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4812     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4813       cav->cave[x][y] = Cblank;
4814
4815   // then copy the real level contents from level file into the playfield
4816   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4817   {
4818     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4819
4820     if (level->field[x][y] == EL_AMOEBA_DEAD)
4821       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4822
4823     cav->cave[x][y] = new_element;
4824   }
4825
4826   for (i = 0; i < MAX_PLAYERS; i++)
4827   {
4828     cav->player_x[i] = -1;
4829     cav->player_y[i] = -1;
4830   }
4831
4832   // initialize player positions and delete players from the playfield
4833   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4834   {
4835     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4836     {
4837       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4838
4839       cav->player_x[player_nr] = x;
4840       cav->player_y[player_nr] = y;
4841
4842       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4843     }
4844   }
4845 }
4846
4847 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4848 {
4849   static int ball_xy[8][2] =
4850   {
4851     { 0, 0 },
4852     { 1, 0 },
4853     { 2, 0 },
4854     { 0, 1 },
4855     { 2, 1 },
4856     { 0, 2 },
4857     { 1, 2 },
4858     { 2, 2 },
4859   };
4860   struct LevelInfo_EM *level_em = level->native_em_level;
4861   struct CAVE *cav = level_em->cav;
4862   int i, j, x, y;
4863
4864   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4865   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4866
4867   level->time        = cav->time_seconds;
4868   level->gems_needed = cav->gems_needed;
4869
4870   sprintf(level->name, "Level %d", level->file_info.nr);
4871
4872   level->score[SC_EMERALD]      = cav->emerald_score;
4873   level->score[SC_DIAMOND]      = cav->diamond_score;
4874   level->score[SC_ROBOT]        = cav->alien_score;
4875   level->score[SC_SPACESHIP]    = cav->tank_score;
4876   level->score[SC_BUG]          = cav->bug_score;
4877   level->score[SC_YAMYAM]       = cav->eater_score;
4878   level->score[SC_NUT]          = cav->nut_score;
4879   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4880   level->score[SC_KEY]          = cav->key_score;
4881   level->score[SC_TIME_BONUS]   = cav->exit_score;
4882
4883   level->num_yamyam_contents    = cav->num_eater_arrays;
4884
4885   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4886     for (y = 0; y < 3; y++)
4887       for (x = 0; x < 3; x++)
4888         level->yamyam_content[i].e[x][y] =
4889           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4890
4891   level->amoeba_speed           = cav->amoeba_time;
4892   level->time_magic_wall        = cav->wonderwall_time;
4893   level->time_wheel             = cav->wheel_time;
4894
4895   level->android_move_time      = cav->android_move_time;
4896   level->android_clone_time     = cav->android_clone_time;
4897   level->ball_random            = cav->ball_random;
4898   level->ball_active_initial    = cav->ball_active;
4899   level->ball_time              = cav->ball_time;
4900   level->num_ball_contents      = cav->num_ball_arrays;
4901
4902   level->lenses_score           = cav->lenses_score;
4903   level->magnify_score          = cav->magnify_score;
4904   level->slurp_score            = cav->slurp_score;
4905
4906   level->lenses_time            = cav->lenses_time;
4907   level->magnify_time           = cav->magnify_time;
4908
4909   level->wind_direction_initial =
4910     map_direction_EM_to_RND(cav->wind_direction);
4911
4912   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4913     for (j = 0; j < 8; j++)
4914       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4915         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4916
4917   map_android_clone_elements_EM_to_RND(level);
4918
4919   // convert the playfield (some elements need special treatment)
4920   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4921   {
4922     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4923
4924     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4925       new_element = EL_AMOEBA_DEAD;
4926
4927     level->field[x][y] = new_element;
4928   }
4929
4930   for (i = 0; i < MAX_PLAYERS; i++)
4931   {
4932     // in case of all players set to the same field, use the first player
4933     int nr = MAX_PLAYERS - i - 1;
4934     int jx = cav->player_x[nr];
4935     int jy = cav->player_y[nr];
4936
4937     if (jx != -1 && jy != -1)
4938       level->field[jx][jy] = EL_PLAYER_1 + nr;
4939   }
4940
4941   // time score is counted for each 10 seconds left in Emerald Mine levels
4942   level->time_score_base = 10;
4943 }
4944
4945
4946 // ----------------------------------------------------------------------------
4947 // functions for loading SP level
4948 // ----------------------------------------------------------------------------
4949
4950 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4951 {
4952   struct LevelInfo_SP *level_sp = level->native_sp_level;
4953   LevelInfoType *header = &level_sp->header;
4954   int i, x, y;
4955
4956   level_sp->width  = level->fieldx;
4957   level_sp->height = level->fieldy;
4958
4959   for (x = 0; x < level->fieldx; x++)
4960     for (y = 0; y < level->fieldy; y++)
4961       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4962
4963   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4964
4965   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4966     header->LevelTitle[i] = level->name[i];
4967   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4968
4969   header->InfotronsNeeded = level->gems_needed;
4970
4971   header->SpecialPortCount = 0;
4972
4973   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4974   {
4975     boolean gravity_port_found = FALSE;
4976     boolean gravity_port_valid = FALSE;
4977     int gravity_port_flag;
4978     int gravity_port_base_element;
4979     int element = level->field[x][y];
4980
4981     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4982         element <= EL_SP_GRAVITY_ON_PORT_UP)
4983     {
4984       gravity_port_found = TRUE;
4985       gravity_port_valid = TRUE;
4986       gravity_port_flag = 1;
4987       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4988     }
4989     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4990              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4991     {
4992       gravity_port_found = TRUE;
4993       gravity_port_valid = TRUE;
4994       gravity_port_flag = 0;
4995       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4996     }
4997     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4998              element <= EL_SP_GRAVITY_PORT_UP)
4999     {
5000       // change R'n'D style gravity inverting special port to normal port
5001       // (there are no gravity inverting ports in native Supaplex engine)
5002
5003       gravity_port_found = TRUE;
5004       gravity_port_valid = FALSE;
5005       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5006     }
5007
5008     if (gravity_port_found)
5009     {
5010       if (gravity_port_valid &&
5011           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5012       {
5013         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5014
5015         port->PortLocation = (y * level->fieldx + x) * 2;
5016         port->Gravity = gravity_port_flag;
5017
5018         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5019
5020         header->SpecialPortCount++;
5021       }
5022       else
5023       {
5024         // change special gravity port to normal port
5025
5026         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5027       }
5028
5029       level_sp->playfield[x][y] = element - EL_SP_START;
5030     }
5031   }
5032 }
5033
5034 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5035 {
5036   struct LevelInfo_SP *level_sp = level->native_sp_level;
5037   LevelInfoType *header = &level_sp->header;
5038   boolean num_invalid_elements = 0;
5039   int i, j, x, y;
5040
5041   level->fieldx = level_sp->width;
5042   level->fieldy = level_sp->height;
5043
5044   for (x = 0; x < level->fieldx; x++)
5045   {
5046     for (y = 0; y < level->fieldy; y++)
5047     {
5048       int element_old = level_sp->playfield[x][y];
5049       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5050
5051       if (element_new == EL_UNKNOWN)
5052       {
5053         num_invalid_elements++;
5054
5055         Debug("level:native:SP", "invalid element %d at position %d, %d",
5056               element_old, x, y);
5057       }
5058
5059       level->field[x][y] = element_new;
5060     }
5061   }
5062
5063   if (num_invalid_elements > 0)
5064     Warn("found %d invalid elements%s", num_invalid_elements,
5065          (!options.debug ? " (use '--debug' for more details)" : ""));
5066
5067   for (i = 0; i < MAX_PLAYERS; i++)
5068     level->initial_player_gravity[i] =
5069       (header->InitialGravity == 1 ? TRUE : FALSE);
5070
5071   // skip leading spaces
5072   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5073     if (header->LevelTitle[i] != ' ')
5074       break;
5075
5076   // copy level title
5077   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5078     level->name[j] = header->LevelTitle[i];
5079   level->name[j] = '\0';
5080
5081   // cut trailing spaces
5082   for (; j > 0; j--)
5083     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5084       level->name[j - 1] = '\0';
5085
5086   level->gems_needed = header->InfotronsNeeded;
5087
5088   for (i = 0; i < header->SpecialPortCount; i++)
5089   {
5090     SpecialPortType *port = &header->SpecialPort[i];
5091     int port_location = port->PortLocation;
5092     int gravity = port->Gravity;
5093     int port_x, port_y, port_element;
5094
5095     port_x = (port_location / 2) % level->fieldx;
5096     port_y = (port_location / 2) / level->fieldx;
5097
5098     if (port_x < 0 || port_x >= level->fieldx ||
5099         port_y < 0 || port_y >= level->fieldy)
5100     {
5101       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5102
5103       continue;
5104     }
5105
5106     port_element = level->field[port_x][port_y];
5107
5108     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5109         port_element > EL_SP_GRAVITY_PORT_UP)
5110     {
5111       Warn("no special port at position (%d, %d)", port_x, port_y);
5112
5113       continue;
5114     }
5115
5116     // change previous (wrong) gravity inverting special port to either
5117     // gravity enabling special port or gravity disabling special port
5118     level->field[port_x][port_y] +=
5119       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5120        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5121   }
5122
5123   // change special gravity ports without database entries to normal ports
5124   for (x = 0; x < level->fieldx; x++)
5125     for (y = 0; y < level->fieldy; y++)
5126       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5127           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5128         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5129
5130   level->time = 0;                      // no time limit
5131   level->amoeba_speed = 0;
5132   level->time_magic_wall = 0;
5133   level->time_wheel = 0;
5134   level->amoeba_content = EL_EMPTY;
5135
5136   // original Supaplex does not use score values -- rate by playing time
5137   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5138     level->score[i] = 0;
5139
5140   level->rate_time_over_score = TRUE;
5141
5142   // there are no yamyams in supaplex levels
5143   for (i = 0; i < level->num_yamyam_contents; i++)
5144     for (x = 0; x < 3; x++)
5145       for (y = 0; y < 3; y++)
5146         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5147 }
5148
5149 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5150 {
5151   struct LevelInfo_SP *level_sp = level->native_sp_level;
5152   struct DemoInfo_SP *demo = &level_sp->demo;
5153   int i, j;
5154
5155   // always start with reliable default values
5156   demo->is_available = FALSE;
5157   demo->length = 0;
5158
5159   if (TAPE_IS_EMPTY(tape))
5160     return;
5161
5162   demo->level_nr = tape.level_nr;       // (currently not used)
5163
5164   level_sp->header.DemoRandomSeed = tape.random_seed;
5165
5166   demo->length = 0;
5167
5168   for (i = 0; i < tape.length; i++)
5169   {
5170     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5171     int demo_repeat = tape.pos[i].delay;
5172     int demo_entries = (demo_repeat + 15) / 16;
5173
5174     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5175     {
5176       Warn("tape truncated: size exceeds maximum SP demo size %d",
5177            SP_MAX_TAPE_LEN);
5178
5179       break;
5180     }
5181
5182     for (j = 0; j < demo_repeat / 16; j++)
5183       demo->data[demo->length++] = 0xf0 | demo_action;
5184
5185     if (demo_repeat % 16)
5186       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5187   }
5188
5189   demo->is_available = TRUE;
5190 }
5191
5192 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5193 {
5194   struct LevelInfo_SP *level_sp = level->native_sp_level;
5195   struct DemoInfo_SP *demo = &level_sp->demo;
5196   char *filename = level->file_info.filename;
5197   int i;
5198
5199   // always start with reliable default values
5200   setTapeInfoToDefaults();
5201
5202   if (!demo->is_available)
5203     return;
5204
5205   tape.level_nr = demo->level_nr;       // (currently not used)
5206   tape.random_seed = level_sp->header.DemoRandomSeed;
5207
5208   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5209
5210   tape.counter = 0;
5211   tape.pos[tape.counter].delay = 0;
5212
5213   for (i = 0; i < demo->length; i++)
5214   {
5215     int demo_action = demo->data[i] & 0x0f;
5216     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5217     int tape_action = map_key_SP_to_RND(demo_action);
5218     int tape_repeat = demo_repeat + 1;
5219     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5220     boolean success = 0;
5221     int j;
5222
5223     for (j = 0; j < tape_repeat; j++)
5224       success = TapeAddAction(action);
5225
5226     if (!success)
5227     {
5228       Warn("SP demo truncated: size exceeds maximum tape size %d",
5229            MAX_TAPE_LEN);
5230
5231       break;
5232     }
5233   }
5234
5235   TapeHaltRecording();
5236 }
5237
5238
5239 // ----------------------------------------------------------------------------
5240 // functions for loading MM level
5241 // ----------------------------------------------------------------------------
5242
5243 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5244 {
5245   struct LevelInfo_MM *level_mm = level->native_mm_level;
5246   int i, x, y;
5247
5248   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5249   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5250
5251   level_mm->time = level->time;
5252   level_mm->kettles_needed = level->gems_needed;
5253   level_mm->auto_count_kettles = level->auto_count_gems;
5254
5255   level_mm->mm_laser_red   = level->mm_laser_red;
5256   level_mm->mm_laser_green = level->mm_laser_green;
5257   level_mm->mm_laser_blue  = level->mm_laser_blue;
5258
5259   level_mm->df_laser_red   = level->df_laser_red;
5260   level_mm->df_laser_green = level->df_laser_green;
5261   level_mm->df_laser_blue  = level->df_laser_blue;
5262
5263   strcpy(level_mm->name, level->name);
5264   strcpy(level_mm->author, level->author);
5265
5266   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5267   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5268   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5269   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5270   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5271
5272   level_mm->amoeba_speed = level->amoeba_speed;
5273   level_mm->time_fuse    = level->mm_time_fuse;
5274   level_mm->time_bomb    = level->mm_time_bomb;
5275   level_mm->time_ball    = level->mm_time_ball;
5276   level_mm->time_block   = level->mm_time_block;
5277
5278   level_mm->num_ball_contents = level->num_mm_ball_contents;
5279   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5280   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5281   level_mm->explode_ball = level->explode_mm_ball;
5282
5283   for (i = 0; i < level->num_mm_ball_contents; i++)
5284     level_mm->ball_content[i] =
5285       map_element_RND_to_MM(level->mm_ball_content[i]);
5286
5287   for (x = 0; x < level->fieldx; x++)
5288     for (y = 0; y < level->fieldy; y++)
5289       Ur[x][y] =
5290         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5291 }
5292
5293 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5294 {
5295   struct LevelInfo_MM *level_mm = level->native_mm_level;
5296   int i, x, y;
5297
5298   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5299   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5300
5301   level->time = level_mm->time;
5302   level->gems_needed = level_mm->kettles_needed;
5303   level->auto_count_gems = level_mm->auto_count_kettles;
5304
5305   level->mm_laser_red   = level_mm->mm_laser_red;
5306   level->mm_laser_green = level_mm->mm_laser_green;
5307   level->mm_laser_blue  = level_mm->mm_laser_blue;
5308
5309   level->df_laser_red   = level_mm->df_laser_red;
5310   level->df_laser_green = level_mm->df_laser_green;
5311   level->df_laser_blue  = level_mm->df_laser_blue;
5312
5313   strcpy(level->name, level_mm->name);
5314
5315   // only overwrite author from 'levelinfo.conf' if author defined in level
5316   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5317     strcpy(level->author, level_mm->author);
5318
5319   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5320   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5321   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5322   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5323   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5324
5325   level->amoeba_speed  = level_mm->amoeba_speed;
5326   level->mm_time_fuse  = level_mm->time_fuse;
5327   level->mm_time_bomb  = level_mm->time_bomb;
5328   level->mm_time_ball  = level_mm->time_ball;
5329   level->mm_time_block = level_mm->time_block;
5330
5331   level->num_mm_ball_contents = level_mm->num_ball_contents;
5332   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5333   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5334   level->explode_mm_ball = level_mm->explode_ball;
5335
5336   for (i = 0; i < level->num_mm_ball_contents; i++)
5337     level->mm_ball_content[i] =
5338       map_element_MM_to_RND(level_mm->ball_content[i]);
5339
5340   for (x = 0; x < level->fieldx; x++)
5341     for (y = 0; y < level->fieldy; y++)
5342       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5343 }
5344
5345
5346 // ----------------------------------------------------------------------------
5347 // functions for loading DC level
5348 // ----------------------------------------------------------------------------
5349
5350 #define DC_LEVEL_HEADER_SIZE            344
5351
5352 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5353                                         boolean init)
5354 {
5355   static int last_data_encoded;
5356   static int offset1;
5357   static int offset2;
5358   int diff;
5359   int diff_hi, diff_lo;
5360   int data_hi, data_lo;
5361   unsigned short data_decoded;
5362
5363   if (init)
5364   {
5365     last_data_encoded = 0;
5366     offset1 = -1;
5367     offset2 = 0;
5368
5369     return 0;
5370   }
5371
5372   diff = data_encoded - last_data_encoded;
5373   diff_hi = diff & ~0xff;
5374   diff_lo = diff &  0xff;
5375
5376   offset2 += diff_lo;
5377
5378   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5379   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5380   data_hi = data_hi & 0xff00;
5381
5382   data_decoded = data_hi | data_lo;
5383
5384   last_data_encoded = data_encoded;
5385
5386   offset1 = (offset1 + 1) % 31;
5387   offset2 = offset2 & 0xff;
5388
5389   return data_decoded;
5390 }
5391
5392 static int getMappedElement_DC(int element)
5393 {
5394   switch (element)
5395   {
5396     case 0x0000:
5397       element = EL_ROCK;
5398       break;
5399
5400       // 0x0117 - 0x036e: (?)
5401       // EL_DIAMOND
5402
5403       // 0x042d - 0x0684: (?)
5404       // EL_EMERALD
5405
5406     case 0x06f1:
5407       element = EL_NUT;
5408       break;
5409
5410     case 0x074c:
5411       element = EL_BOMB;
5412       break;
5413
5414     case 0x07a4:
5415       element = EL_PEARL;
5416       break;
5417
5418     case 0x0823:
5419       element = EL_CRYSTAL;
5420       break;
5421
5422     case 0x0e77:        // quicksand (boulder)
5423       element = EL_QUICKSAND_FAST_FULL;
5424       break;
5425
5426     case 0x0e99:        // slow quicksand (boulder)
5427       element = EL_QUICKSAND_FULL;
5428       break;
5429
5430     case 0x0ed2:
5431       element = EL_EM_EXIT_OPEN;
5432       break;
5433
5434     case 0x0ee3:
5435       element = EL_EM_EXIT_CLOSED;
5436       break;
5437
5438     case 0x0eeb:
5439       element = EL_EM_STEEL_EXIT_OPEN;
5440       break;
5441
5442     case 0x0efc:
5443       element = EL_EM_STEEL_EXIT_CLOSED;
5444       break;
5445
5446     case 0x0f4f:        // dynamite (lit 1)
5447       element = EL_EM_DYNAMITE_ACTIVE;
5448       break;
5449
5450     case 0x0f57:        // dynamite (lit 2)
5451       element = EL_EM_DYNAMITE_ACTIVE;
5452       break;
5453
5454     case 0x0f5f:        // dynamite (lit 3)
5455       element = EL_EM_DYNAMITE_ACTIVE;
5456       break;
5457
5458     case 0x0f67:        // dynamite (lit 4)
5459       element = EL_EM_DYNAMITE_ACTIVE;
5460       break;
5461
5462     case 0x0f81:
5463     case 0x0f82:
5464     case 0x0f83:
5465     case 0x0f84:
5466       element = EL_AMOEBA_WET;
5467       break;
5468
5469     case 0x0f85:
5470       element = EL_AMOEBA_DROP;
5471       break;
5472
5473     case 0x0fb9:
5474       element = EL_DC_MAGIC_WALL;
5475       break;
5476
5477     case 0x0fd0:
5478       element = EL_SPACESHIP_UP;
5479       break;
5480
5481     case 0x0fd9:
5482       element = EL_SPACESHIP_DOWN;
5483       break;
5484
5485     case 0x0ff1:
5486       element = EL_SPACESHIP_LEFT;
5487       break;
5488
5489     case 0x0ff9:
5490       element = EL_SPACESHIP_RIGHT;
5491       break;
5492
5493     case 0x1057:
5494       element = EL_BUG_UP;
5495       break;
5496
5497     case 0x1060:
5498       element = EL_BUG_DOWN;
5499       break;
5500
5501     case 0x1078:
5502       element = EL_BUG_LEFT;
5503       break;
5504
5505     case 0x1080:
5506       element = EL_BUG_RIGHT;
5507       break;
5508
5509     case 0x10de:
5510       element = EL_MOLE_UP;
5511       break;
5512
5513     case 0x10e7:
5514       element = EL_MOLE_DOWN;
5515       break;
5516
5517     case 0x10ff:
5518       element = EL_MOLE_LEFT;
5519       break;
5520
5521     case 0x1107:
5522       element = EL_MOLE_RIGHT;
5523       break;
5524
5525     case 0x11c0:
5526       element = EL_ROBOT;
5527       break;
5528
5529     case 0x13f5:
5530       element = EL_YAMYAM_UP;
5531       break;
5532
5533     case 0x1425:
5534       element = EL_SWITCHGATE_OPEN;
5535       break;
5536
5537     case 0x1426:
5538       element = EL_SWITCHGATE_CLOSED;
5539       break;
5540
5541     case 0x1437:
5542       element = EL_DC_SWITCHGATE_SWITCH_UP;
5543       break;
5544
5545     case 0x143a:
5546       element = EL_TIMEGATE_CLOSED;
5547       break;
5548
5549     case 0x144c:        // conveyor belt switch (green)
5550       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5551       break;
5552
5553     case 0x144f:        // conveyor belt switch (red)
5554       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5555       break;
5556
5557     case 0x1452:        // conveyor belt switch (blue)
5558       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5559       break;
5560
5561     case 0x145b:
5562       element = EL_CONVEYOR_BELT_3_MIDDLE;
5563       break;
5564
5565     case 0x1463:
5566       element = EL_CONVEYOR_BELT_3_LEFT;
5567       break;
5568
5569     case 0x146b:
5570       element = EL_CONVEYOR_BELT_3_RIGHT;
5571       break;
5572
5573     case 0x1473:
5574       element = EL_CONVEYOR_BELT_1_MIDDLE;
5575       break;
5576
5577     case 0x147b:
5578       element = EL_CONVEYOR_BELT_1_LEFT;
5579       break;
5580
5581     case 0x1483:
5582       element = EL_CONVEYOR_BELT_1_RIGHT;
5583       break;
5584
5585     case 0x148b:
5586       element = EL_CONVEYOR_BELT_4_MIDDLE;
5587       break;
5588
5589     case 0x1493:
5590       element = EL_CONVEYOR_BELT_4_LEFT;
5591       break;
5592
5593     case 0x149b:
5594       element = EL_CONVEYOR_BELT_4_RIGHT;
5595       break;
5596
5597     case 0x14ac:
5598       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5599       break;
5600
5601     case 0x14bd:
5602       element = EL_EXPANDABLE_WALL_VERTICAL;
5603       break;
5604
5605     case 0x14c6:
5606       element = EL_EXPANDABLE_WALL_ANY;
5607       break;
5608
5609     case 0x14ce:        // growing steel wall (left/right)
5610       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5611       break;
5612
5613     case 0x14df:        // growing steel wall (up/down)
5614       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5615       break;
5616
5617     case 0x14e8:        // growing steel wall (up/down/left/right)
5618       element = EL_EXPANDABLE_STEELWALL_ANY;
5619       break;
5620
5621     case 0x14e9:
5622       element = EL_SHIELD_DEADLY;
5623       break;
5624
5625     case 0x1501:
5626       element = EL_EXTRA_TIME;
5627       break;
5628
5629     case 0x154f:
5630       element = EL_ACID;
5631       break;
5632
5633     case 0x1577:
5634       element = EL_EMPTY_SPACE;
5635       break;
5636
5637     case 0x1578:        // quicksand (empty)
5638       element = EL_QUICKSAND_FAST_EMPTY;
5639       break;
5640
5641     case 0x1579:        // slow quicksand (empty)
5642       element = EL_QUICKSAND_EMPTY;
5643       break;
5644
5645       // 0x157c - 0x158b:
5646       // EL_SAND
5647
5648       // 0x1590 - 0x159f:
5649       // EL_DC_LANDMINE
5650
5651     case 0x15a0:
5652       element = EL_EM_DYNAMITE;
5653       break;
5654
5655     case 0x15a1:        // key (red)
5656       element = EL_EM_KEY_1;
5657       break;
5658
5659     case 0x15a2:        // key (yellow)
5660       element = EL_EM_KEY_2;
5661       break;
5662
5663     case 0x15a3:        // key (blue)
5664       element = EL_EM_KEY_4;
5665       break;
5666
5667     case 0x15a4:        // key (green)
5668       element = EL_EM_KEY_3;
5669       break;
5670
5671     case 0x15a5:        // key (white)
5672       element = EL_DC_KEY_WHITE;
5673       break;
5674
5675     case 0x15a6:
5676       element = EL_WALL_SLIPPERY;
5677       break;
5678
5679     case 0x15a7:
5680       element = EL_WALL;
5681       break;
5682
5683     case 0x15a8:        // wall (not round)
5684       element = EL_WALL;
5685       break;
5686
5687     case 0x15a9:        // (blue)
5688       element = EL_CHAR_A;
5689       break;
5690
5691     case 0x15aa:        // (blue)
5692       element = EL_CHAR_B;
5693       break;
5694
5695     case 0x15ab:        // (blue)
5696       element = EL_CHAR_C;
5697       break;
5698
5699     case 0x15ac:        // (blue)
5700       element = EL_CHAR_D;
5701       break;
5702
5703     case 0x15ad:        // (blue)
5704       element = EL_CHAR_E;
5705       break;
5706
5707     case 0x15ae:        // (blue)
5708       element = EL_CHAR_F;
5709       break;
5710
5711     case 0x15af:        // (blue)
5712       element = EL_CHAR_G;
5713       break;
5714
5715     case 0x15b0:        // (blue)
5716       element = EL_CHAR_H;
5717       break;
5718
5719     case 0x15b1:        // (blue)
5720       element = EL_CHAR_I;
5721       break;
5722
5723     case 0x15b2:        // (blue)
5724       element = EL_CHAR_J;
5725       break;
5726
5727     case 0x15b3:        // (blue)
5728       element = EL_CHAR_K;
5729       break;
5730
5731     case 0x15b4:        // (blue)
5732       element = EL_CHAR_L;
5733       break;
5734
5735     case 0x15b5:        // (blue)
5736       element = EL_CHAR_M;
5737       break;
5738
5739     case 0x15b6:        // (blue)
5740       element = EL_CHAR_N;
5741       break;
5742
5743     case 0x15b7:        // (blue)
5744       element = EL_CHAR_O;
5745       break;
5746
5747     case 0x15b8:        // (blue)
5748       element = EL_CHAR_P;
5749       break;
5750
5751     case 0x15b9:        // (blue)
5752       element = EL_CHAR_Q;
5753       break;
5754
5755     case 0x15ba:        // (blue)
5756       element = EL_CHAR_R;
5757       break;
5758
5759     case 0x15bb:        // (blue)
5760       element = EL_CHAR_S;
5761       break;
5762
5763     case 0x15bc:        // (blue)
5764       element = EL_CHAR_T;
5765       break;
5766
5767     case 0x15bd:        // (blue)
5768       element = EL_CHAR_U;
5769       break;
5770
5771     case 0x15be:        // (blue)
5772       element = EL_CHAR_V;
5773       break;
5774
5775     case 0x15bf:        // (blue)
5776       element = EL_CHAR_W;
5777       break;
5778
5779     case 0x15c0:        // (blue)
5780       element = EL_CHAR_X;
5781       break;
5782
5783     case 0x15c1:        // (blue)
5784       element = EL_CHAR_Y;
5785       break;
5786
5787     case 0x15c2:        // (blue)
5788       element = EL_CHAR_Z;
5789       break;
5790
5791     case 0x15c3:        // (blue)
5792       element = EL_CHAR_AUMLAUT;
5793       break;
5794
5795     case 0x15c4:        // (blue)
5796       element = EL_CHAR_OUMLAUT;
5797       break;
5798
5799     case 0x15c5:        // (blue)
5800       element = EL_CHAR_UUMLAUT;
5801       break;
5802
5803     case 0x15c6:        // (blue)
5804       element = EL_CHAR_0;
5805       break;
5806
5807     case 0x15c7:        // (blue)
5808       element = EL_CHAR_1;
5809       break;
5810
5811     case 0x15c8:        // (blue)
5812       element = EL_CHAR_2;
5813       break;
5814
5815     case 0x15c9:        // (blue)
5816       element = EL_CHAR_3;
5817       break;
5818
5819     case 0x15ca:        // (blue)
5820       element = EL_CHAR_4;
5821       break;
5822
5823     case 0x15cb:        // (blue)
5824       element = EL_CHAR_5;
5825       break;
5826
5827     case 0x15cc:        // (blue)
5828       element = EL_CHAR_6;
5829       break;
5830
5831     case 0x15cd:        // (blue)
5832       element = EL_CHAR_7;
5833       break;
5834
5835     case 0x15ce:        // (blue)
5836       element = EL_CHAR_8;
5837       break;
5838
5839     case 0x15cf:        // (blue)
5840       element = EL_CHAR_9;
5841       break;
5842
5843     case 0x15d0:        // (blue)
5844       element = EL_CHAR_PERIOD;
5845       break;
5846
5847     case 0x15d1:        // (blue)
5848       element = EL_CHAR_EXCLAM;
5849       break;
5850
5851     case 0x15d2:        // (blue)
5852       element = EL_CHAR_COLON;
5853       break;
5854
5855     case 0x15d3:        // (blue)
5856       element = EL_CHAR_LESS;
5857       break;
5858
5859     case 0x15d4:        // (blue)
5860       element = EL_CHAR_GREATER;
5861       break;
5862
5863     case 0x15d5:        // (blue)
5864       element = EL_CHAR_QUESTION;
5865       break;
5866
5867     case 0x15d6:        // (blue)
5868       element = EL_CHAR_COPYRIGHT;
5869       break;
5870
5871     case 0x15d7:        // (blue)
5872       element = EL_CHAR_UP;
5873       break;
5874
5875     case 0x15d8:        // (blue)
5876       element = EL_CHAR_DOWN;
5877       break;
5878
5879     case 0x15d9:        // (blue)
5880       element = EL_CHAR_BUTTON;
5881       break;
5882
5883     case 0x15da:        // (blue)
5884       element = EL_CHAR_PLUS;
5885       break;
5886
5887     case 0x15db:        // (blue)
5888       element = EL_CHAR_MINUS;
5889       break;
5890
5891     case 0x15dc:        // (blue)
5892       element = EL_CHAR_APOSTROPHE;
5893       break;
5894
5895     case 0x15dd:        // (blue)
5896       element = EL_CHAR_PARENLEFT;
5897       break;
5898
5899     case 0x15de:        // (blue)
5900       element = EL_CHAR_PARENRIGHT;
5901       break;
5902
5903     case 0x15df:        // (green)
5904       element = EL_CHAR_A;
5905       break;
5906
5907     case 0x15e0:        // (green)
5908       element = EL_CHAR_B;
5909       break;
5910
5911     case 0x15e1:        // (green)
5912       element = EL_CHAR_C;
5913       break;
5914
5915     case 0x15e2:        // (green)
5916       element = EL_CHAR_D;
5917       break;
5918
5919     case 0x15e3:        // (green)
5920       element = EL_CHAR_E;
5921       break;
5922
5923     case 0x15e4:        // (green)
5924       element = EL_CHAR_F;
5925       break;
5926
5927     case 0x15e5:        // (green)
5928       element = EL_CHAR_G;
5929       break;
5930
5931     case 0x15e6:        // (green)
5932       element = EL_CHAR_H;
5933       break;
5934
5935     case 0x15e7:        // (green)
5936       element = EL_CHAR_I;
5937       break;
5938
5939     case 0x15e8:        // (green)
5940       element = EL_CHAR_J;
5941       break;
5942
5943     case 0x15e9:        // (green)
5944       element = EL_CHAR_K;
5945       break;
5946
5947     case 0x15ea:        // (green)
5948       element = EL_CHAR_L;
5949       break;
5950
5951     case 0x15eb:        // (green)
5952       element = EL_CHAR_M;
5953       break;
5954
5955     case 0x15ec:        // (green)
5956       element = EL_CHAR_N;
5957       break;
5958
5959     case 0x15ed:        // (green)
5960       element = EL_CHAR_O;
5961       break;
5962
5963     case 0x15ee:        // (green)
5964       element = EL_CHAR_P;
5965       break;
5966
5967     case 0x15ef:        // (green)
5968       element = EL_CHAR_Q;
5969       break;
5970
5971     case 0x15f0:        // (green)
5972       element = EL_CHAR_R;
5973       break;
5974
5975     case 0x15f1:        // (green)
5976       element = EL_CHAR_S;
5977       break;
5978
5979     case 0x15f2:        // (green)
5980       element = EL_CHAR_T;
5981       break;
5982
5983     case 0x15f3:        // (green)
5984       element = EL_CHAR_U;
5985       break;
5986
5987     case 0x15f4:        // (green)
5988       element = EL_CHAR_V;
5989       break;
5990
5991     case 0x15f5:        // (green)
5992       element = EL_CHAR_W;
5993       break;
5994
5995     case 0x15f6:        // (green)
5996       element = EL_CHAR_X;
5997       break;
5998
5999     case 0x15f7:        // (green)
6000       element = EL_CHAR_Y;
6001       break;
6002
6003     case 0x15f8:        // (green)
6004       element = EL_CHAR_Z;
6005       break;
6006
6007     case 0x15f9:        // (green)
6008       element = EL_CHAR_AUMLAUT;
6009       break;
6010
6011     case 0x15fa:        // (green)
6012       element = EL_CHAR_OUMLAUT;
6013       break;
6014
6015     case 0x15fb:        // (green)
6016       element = EL_CHAR_UUMLAUT;
6017       break;
6018
6019     case 0x15fc:        // (green)
6020       element = EL_CHAR_0;
6021       break;
6022
6023     case 0x15fd:        // (green)
6024       element = EL_CHAR_1;
6025       break;
6026
6027     case 0x15fe:        // (green)
6028       element = EL_CHAR_2;
6029       break;
6030
6031     case 0x15ff:        // (green)
6032       element = EL_CHAR_3;
6033       break;
6034
6035     case 0x1600:        // (green)
6036       element = EL_CHAR_4;
6037       break;
6038
6039     case 0x1601:        // (green)
6040       element = EL_CHAR_5;
6041       break;
6042
6043     case 0x1602:        // (green)
6044       element = EL_CHAR_6;
6045       break;
6046
6047     case 0x1603:        // (green)
6048       element = EL_CHAR_7;
6049       break;
6050
6051     case 0x1604:        // (green)
6052       element = EL_CHAR_8;
6053       break;
6054
6055     case 0x1605:        // (green)
6056       element = EL_CHAR_9;
6057       break;
6058
6059     case 0x1606:        // (green)
6060       element = EL_CHAR_PERIOD;
6061       break;
6062
6063     case 0x1607:        // (green)
6064       element = EL_CHAR_EXCLAM;
6065       break;
6066
6067     case 0x1608:        // (green)
6068       element = EL_CHAR_COLON;
6069       break;
6070
6071     case 0x1609:        // (green)
6072       element = EL_CHAR_LESS;
6073       break;
6074
6075     case 0x160a:        // (green)
6076       element = EL_CHAR_GREATER;
6077       break;
6078
6079     case 0x160b:        // (green)
6080       element = EL_CHAR_QUESTION;
6081       break;
6082
6083     case 0x160c:        // (green)
6084       element = EL_CHAR_COPYRIGHT;
6085       break;
6086
6087     case 0x160d:        // (green)
6088       element = EL_CHAR_UP;
6089       break;
6090
6091     case 0x160e:        // (green)
6092       element = EL_CHAR_DOWN;
6093       break;
6094
6095     case 0x160f:        // (green)
6096       element = EL_CHAR_BUTTON;
6097       break;
6098
6099     case 0x1610:        // (green)
6100       element = EL_CHAR_PLUS;
6101       break;
6102
6103     case 0x1611:        // (green)
6104       element = EL_CHAR_MINUS;
6105       break;
6106
6107     case 0x1612:        // (green)
6108       element = EL_CHAR_APOSTROPHE;
6109       break;
6110
6111     case 0x1613:        // (green)
6112       element = EL_CHAR_PARENLEFT;
6113       break;
6114
6115     case 0x1614:        // (green)
6116       element = EL_CHAR_PARENRIGHT;
6117       break;
6118
6119     case 0x1615:        // (blue steel)
6120       element = EL_STEEL_CHAR_A;
6121       break;
6122
6123     case 0x1616:        // (blue steel)
6124       element = EL_STEEL_CHAR_B;
6125       break;
6126
6127     case 0x1617:        // (blue steel)
6128       element = EL_STEEL_CHAR_C;
6129       break;
6130
6131     case 0x1618:        // (blue steel)
6132       element = EL_STEEL_CHAR_D;
6133       break;
6134
6135     case 0x1619:        // (blue steel)
6136       element = EL_STEEL_CHAR_E;
6137       break;
6138
6139     case 0x161a:        // (blue steel)
6140       element = EL_STEEL_CHAR_F;
6141       break;
6142
6143     case 0x161b:        // (blue steel)
6144       element = EL_STEEL_CHAR_G;
6145       break;
6146
6147     case 0x161c:        // (blue steel)
6148       element = EL_STEEL_CHAR_H;
6149       break;
6150
6151     case 0x161d:        // (blue steel)
6152       element = EL_STEEL_CHAR_I;
6153       break;
6154
6155     case 0x161e:        // (blue steel)
6156       element = EL_STEEL_CHAR_J;
6157       break;
6158
6159     case 0x161f:        // (blue steel)
6160       element = EL_STEEL_CHAR_K;
6161       break;
6162
6163     case 0x1620:        // (blue steel)
6164       element = EL_STEEL_CHAR_L;
6165       break;
6166
6167     case 0x1621:        // (blue steel)
6168       element = EL_STEEL_CHAR_M;
6169       break;
6170
6171     case 0x1622:        // (blue steel)
6172       element = EL_STEEL_CHAR_N;
6173       break;
6174
6175     case 0x1623:        // (blue steel)
6176       element = EL_STEEL_CHAR_O;
6177       break;
6178
6179     case 0x1624:        // (blue steel)
6180       element = EL_STEEL_CHAR_P;
6181       break;
6182
6183     case 0x1625:        // (blue steel)
6184       element = EL_STEEL_CHAR_Q;
6185       break;
6186
6187     case 0x1626:        // (blue steel)
6188       element = EL_STEEL_CHAR_R;
6189       break;
6190
6191     case 0x1627:        // (blue steel)
6192       element = EL_STEEL_CHAR_S;
6193       break;
6194
6195     case 0x1628:        // (blue steel)
6196       element = EL_STEEL_CHAR_T;
6197       break;
6198
6199     case 0x1629:        // (blue steel)
6200       element = EL_STEEL_CHAR_U;
6201       break;
6202
6203     case 0x162a:        // (blue steel)
6204       element = EL_STEEL_CHAR_V;
6205       break;
6206
6207     case 0x162b:        // (blue steel)
6208       element = EL_STEEL_CHAR_W;
6209       break;
6210
6211     case 0x162c:        // (blue steel)
6212       element = EL_STEEL_CHAR_X;
6213       break;
6214
6215     case 0x162d:        // (blue steel)
6216       element = EL_STEEL_CHAR_Y;
6217       break;
6218
6219     case 0x162e:        // (blue steel)
6220       element = EL_STEEL_CHAR_Z;
6221       break;
6222
6223     case 0x162f:        // (blue steel)
6224       element = EL_STEEL_CHAR_AUMLAUT;
6225       break;
6226
6227     case 0x1630:        // (blue steel)
6228       element = EL_STEEL_CHAR_OUMLAUT;
6229       break;
6230
6231     case 0x1631:        // (blue steel)
6232       element = EL_STEEL_CHAR_UUMLAUT;
6233       break;
6234
6235     case 0x1632:        // (blue steel)
6236       element = EL_STEEL_CHAR_0;
6237       break;
6238
6239     case 0x1633:        // (blue steel)
6240       element = EL_STEEL_CHAR_1;
6241       break;
6242
6243     case 0x1634:        // (blue steel)
6244       element = EL_STEEL_CHAR_2;
6245       break;
6246
6247     case 0x1635:        // (blue steel)
6248       element = EL_STEEL_CHAR_3;
6249       break;
6250
6251     case 0x1636:        // (blue steel)
6252       element = EL_STEEL_CHAR_4;
6253       break;
6254
6255     case 0x1637:        // (blue steel)
6256       element = EL_STEEL_CHAR_5;
6257       break;
6258
6259     case 0x1638:        // (blue steel)
6260       element = EL_STEEL_CHAR_6;
6261       break;
6262
6263     case 0x1639:        // (blue steel)
6264       element = EL_STEEL_CHAR_7;
6265       break;
6266
6267     case 0x163a:        // (blue steel)
6268       element = EL_STEEL_CHAR_8;
6269       break;
6270
6271     case 0x163b:        // (blue steel)
6272       element = EL_STEEL_CHAR_9;
6273       break;
6274
6275     case 0x163c:        // (blue steel)
6276       element = EL_STEEL_CHAR_PERIOD;
6277       break;
6278
6279     case 0x163d:        // (blue steel)
6280       element = EL_STEEL_CHAR_EXCLAM;
6281       break;
6282
6283     case 0x163e:        // (blue steel)
6284       element = EL_STEEL_CHAR_COLON;
6285       break;
6286
6287     case 0x163f:        // (blue steel)
6288       element = EL_STEEL_CHAR_LESS;
6289       break;
6290
6291     case 0x1640:        // (blue steel)
6292       element = EL_STEEL_CHAR_GREATER;
6293       break;
6294
6295     case 0x1641:        // (blue steel)
6296       element = EL_STEEL_CHAR_QUESTION;
6297       break;
6298
6299     case 0x1642:        // (blue steel)
6300       element = EL_STEEL_CHAR_COPYRIGHT;
6301       break;
6302
6303     case 0x1643:        // (blue steel)
6304       element = EL_STEEL_CHAR_UP;
6305       break;
6306
6307     case 0x1644:        // (blue steel)
6308       element = EL_STEEL_CHAR_DOWN;
6309       break;
6310
6311     case 0x1645:        // (blue steel)
6312       element = EL_STEEL_CHAR_BUTTON;
6313       break;
6314
6315     case 0x1646:        // (blue steel)
6316       element = EL_STEEL_CHAR_PLUS;
6317       break;
6318
6319     case 0x1647:        // (blue steel)
6320       element = EL_STEEL_CHAR_MINUS;
6321       break;
6322
6323     case 0x1648:        // (blue steel)
6324       element = EL_STEEL_CHAR_APOSTROPHE;
6325       break;
6326
6327     case 0x1649:        // (blue steel)
6328       element = EL_STEEL_CHAR_PARENLEFT;
6329       break;
6330
6331     case 0x164a:        // (blue steel)
6332       element = EL_STEEL_CHAR_PARENRIGHT;
6333       break;
6334
6335     case 0x164b:        // (green steel)
6336       element = EL_STEEL_CHAR_A;
6337       break;
6338
6339     case 0x164c:        // (green steel)
6340       element = EL_STEEL_CHAR_B;
6341       break;
6342
6343     case 0x164d:        // (green steel)
6344       element = EL_STEEL_CHAR_C;
6345       break;
6346
6347     case 0x164e:        // (green steel)
6348       element = EL_STEEL_CHAR_D;
6349       break;
6350
6351     case 0x164f:        // (green steel)
6352       element = EL_STEEL_CHAR_E;
6353       break;
6354
6355     case 0x1650:        // (green steel)
6356       element = EL_STEEL_CHAR_F;
6357       break;
6358
6359     case 0x1651:        // (green steel)
6360       element = EL_STEEL_CHAR_G;
6361       break;
6362
6363     case 0x1652:        // (green steel)
6364       element = EL_STEEL_CHAR_H;
6365       break;
6366
6367     case 0x1653:        // (green steel)
6368       element = EL_STEEL_CHAR_I;
6369       break;
6370
6371     case 0x1654:        // (green steel)
6372       element = EL_STEEL_CHAR_J;
6373       break;
6374
6375     case 0x1655:        // (green steel)
6376       element = EL_STEEL_CHAR_K;
6377       break;
6378
6379     case 0x1656:        // (green steel)
6380       element = EL_STEEL_CHAR_L;
6381       break;
6382
6383     case 0x1657:        // (green steel)
6384       element = EL_STEEL_CHAR_M;
6385       break;
6386
6387     case 0x1658:        // (green steel)
6388       element = EL_STEEL_CHAR_N;
6389       break;
6390
6391     case 0x1659:        // (green steel)
6392       element = EL_STEEL_CHAR_O;
6393       break;
6394
6395     case 0x165a:        // (green steel)
6396       element = EL_STEEL_CHAR_P;
6397       break;
6398
6399     case 0x165b:        // (green steel)
6400       element = EL_STEEL_CHAR_Q;
6401       break;
6402
6403     case 0x165c:        // (green steel)
6404       element = EL_STEEL_CHAR_R;
6405       break;
6406
6407     case 0x165d:        // (green steel)
6408       element = EL_STEEL_CHAR_S;
6409       break;
6410
6411     case 0x165e:        // (green steel)
6412       element = EL_STEEL_CHAR_T;
6413       break;
6414
6415     case 0x165f:        // (green steel)
6416       element = EL_STEEL_CHAR_U;
6417       break;
6418
6419     case 0x1660:        // (green steel)
6420       element = EL_STEEL_CHAR_V;
6421       break;
6422
6423     case 0x1661:        // (green steel)
6424       element = EL_STEEL_CHAR_W;
6425       break;
6426
6427     case 0x1662:        // (green steel)
6428       element = EL_STEEL_CHAR_X;
6429       break;
6430
6431     case 0x1663:        // (green steel)
6432       element = EL_STEEL_CHAR_Y;
6433       break;
6434
6435     case 0x1664:        // (green steel)
6436       element = EL_STEEL_CHAR_Z;
6437       break;
6438
6439     case 0x1665:        // (green steel)
6440       element = EL_STEEL_CHAR_AUMLAUT;
6441       break;
6442
6443     case 0x1666:        // (green steel)
6444       element = EL_STEEL_CHAR_OUMLAUT;
6445       break;
6446
6447     case 0x1667:        // (green steel)
6448       element = EL_STEEL_CHAR_UUMLAUT;
6449       break;
6450
6451     case 0x1668:        // (green steel)
6452       element = EL_STEEL_CHAR_0;
6453       break;
6454
6455     case 0x1669:        // (green steel)
6456       element = EL_STEEL_CHAR_1;
6457       break;
6458
6459     case 0x166a:        // (green steel)
6460       element = EL_STEEL_CHAR_2;
6461       break;
6462
6463     case 0x166b:        // (green steel)
6464       element = EL_STEEL_CHAR_3;
6465       break;
6466
6467     case 0x166c:        // (green steel)
6468       element = EL_STEEL_CHAR_4;
6469       break;
6470
6471     case 0x166d:        // (green steel)
6472       element = EL_STEEL_CHAR_5;
6473       break;
6474
6475     case 0x166e:        // (green steel)
6476       element = EL_STEEL_CHAR_6;
6477       break;
6478
6479     case 0x166f:        // (green steel)
6480       element = EL_STEEL_CHAR_7;
6481       break;
6482
6483     case 0x1670:        // (green steel)
6484       element = EL_STEEL_CHAR_8;
6485       break;
6486
6487     case 0x1671:        // (green steel)
6488       element = EL_STEEL_CHAR_9;
6489       break;
6490
6491     case 0x1672:        // (green steel)
6492       element = EL_STEEL_CHAR_PERIOD;
6493       break;
6494
6495     case 0x1673:        // (green steel)
6496       element = EL_STEEL_CHAR_EXCLAM;
6497       break;
6498
6499     case 0x1674:        // (green steel)
6500       element = EL_STEEL_CHAR_COLON;
6501       break;
6502
6503     case 0x1675:        // (green steel)
6504       element = EL_STEEL_CHAR_LESS;
6505       break;
6506
6507     case 0x1676:        // (green steel)
6508       element = EL_STEEL_CHAR_GREATER;
6509       break;
6510
6511     case 0x1677:        // (green steel)
6512       element = EL_STEEL_CHAR_QUESTION;
6513       break;
6514
6515     case 0x1678:        // (green steel)
6516       element = EL_STEEL_CHAR_COPYRIGHT;
6517       break;
6518
6519     case 0x1679:        // (green steel)
6520       element = EL_STEEL_CHAR_UP;
6521       break;
6522
6523     case 0x167a:        // (green steel)
6524       element = EL_STEEL_CHAR_DOWN;
6525       break;
6526
6527     case 0x167b:        // (green steel)
6528       element = EL_STEEL_CHAR_BUTTON;
6529       break;
6530
6531     case 0x167c:        // (green steel)
6532       element = EL_STEEL_CHAR_PLUS;
6533       break;
6534
6535     case 0x167d:        // (green steel)
6536       element = EL_STEEL_CHAR_MINUS;
6537       break;
6538
6539     case 0x167e:        // (green steel)
6540       element = EL_STEEL_CHAR_APOSTROPHE;
6541       break;
6542
6543     case 0x167f:        // (green steel)
6544       element = EL_STEEL_CHAR_PARENLEFT;
6545       break;
6546
6547     case 0x1680:        // (green steel)
6548       element = EL_STEEL_CHAR_PARENRIGHT;
6549       break;
6550
6551     case 0x1681:        // gate (red)
6552       element = EL_EM_GATE_1;
6553       break;
6554
6555     case 0x1682:        // secret gate (red)
6556       element = EL_EM_GATE_1_GRAY;
6557       break;
6558
6559     case 0x1683:        // gate (yellow)
6560       element = EL_EM_GATE_2;
6561       break;
6562
6563     case 0x1684:        // secret gate (yellow)
6564       element = EL_EM_GATE_2_GRAY;
6565       break;
6566
6567     case 0x1685:        // gate (blue)
6568       element = EL_EM_GATE_4;
6569       break;
6570
6571     case 0x1686:        // secret gate (blue)
6572       element = EL_EM_GATE_4_GRAY;
6573       break;
6574
6575     case 0x1687:        // gate (green)
6576       element = EL_EM_GATE_3;
6577       break;
6578
6579     case 0x1688:        // secret gate (green)
6580       element = EL_EM_GATE_3_GRAY;
6581       break;
6582
6583     case 0x1689:        // gate (white)
6584       element = EL_DC_GATE_WHITE;
6585       break;
6586
6587     case 0x168a:        // secret gate (white)
6588       element = EL_DC_GATE_WHITE_GRAY;
6589       break;
6590
6591     case 0x168b:        // secret gate (no key)
6592       element = EL_DC_GATE_FAKE_GRAY;
6593       break;
6594
6595     case 0x168c:
6596       element = EL_ROBOT_WHEEL;
6597       break;
6598
6599     case 0x168d:
6600       element = EL_DC_TIMEGATE_SWITCH;
6601       break;
6602
6603     case 0x168e:
6604       element = EL_ACID_POOL_BOTTOM;
6605       break;
6606
6607     case 0x168f:
6608       element = EL_ACID_POOL_TOPLEFT;
6609       break;
6610
6611     case 0x1690:
6612       element = EL_ACID_POOL_TOPRIGHT;
6613       break;
6614
6615     case 0x1691:
6616       element = EL_ACID_POOL_BOTTOMLEFT;
6617       break;
6618
6619     case 0x1692:
6620       element = EL_ACID_POOL_BOTTOMRIGHT;
6621       break;
6622
6623     case 0x1693:
6624       element = EL_STEELWALL;
6625       break;
6626
6627     case 0x1694:
6628       element = EL_STEELWALL_SLIPPERY;
6629       break;
6630
6631     case 0x1695:        // steel wall (not round)
6632       element = EL_STEELWALL;
6633       break;
6634
6635     case 0x1696:        // steel wall (left)
6636       element = EL_DC_STEELWALL_1_LEFT;
6637       break;
6638
6639     case 0x1697:        // steel wall (bottom)
6640       element = EL_DC_STEELWALL_1_BOTTOM;
6641       break;
6642
6643     case 0x1698:        // steel wall (right)
6644       element = EL_DC_STEELWALL_1_RIGHT;
6645       break;
6646
6647     case 0x1699:        // steel wall (top)
6648       element = EL_DC_STEELWALL_1_TOP;
6649       break;
6650
6651     case 0x169a:        // steel wall (left/bottom)
6652       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6653       break;
6654
6655     case 0x169b:        // steel wall (right/bottom)
6656       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6657       break;
6658
6659     case 0x169c:        // steel wall (right/top)
6660       element = EL_DC_STEELWALL_1_TOPRIGHT;
6661       break;
6662
6663     case 0x169d:        // steel wall (left/top)
6664       element = EL_DC_STEELWALL_1_TOPLEFT;
6665       break;
6666
6667     case 0x169e:        // steel wall (right/bottom small)
6668       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6669       break;
6670
6671     case 0x169f:        // steel wall (left/bottom small)
6672       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6673       break;
6674
6675     case 0x16a0:        // steel wall (right/top small)
6676       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6677       break;
6678
6679     case 0x16a1:        // steel wall (left/top small)
6680       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6681       break;
6682
6683     case 0x16a2:        // steel wall (left/right)
6684       element = EL_DC_STEELWALL_1_VERTICAL;
6685       break;
6686
6687     case 0x16a3:        // steel wall (top/bottom)
6688       element = EL_DC_STEELWALL_1_HORIZONTAL;
6689       break;
6690
6691     case 0x16a4:        // steel wall 2 (left end)
6692       element = EL_DC_STEELWALL_2_LEFT;
6693       break;
6694
6695     case 0x16a5:        // steel wall 2 (right end)
6696       element = EL_DC_STEELWALL_2_RIGHT;
6697       break;
6698
6699     case 0x16a6:        // steel wall 2 (top end)
6700       element = EL_DC_STEELWALL_2_TOP;
6701       break;
6702
6703     case 0x16a7:        // steel wall 2 (bottom end)
6704       element = EL_DC_STEELWALL_2_BOTTOM;
6705       break;
6706
6707     case 0x16a8:        // steel wall 2 (left/right)
6708       element = EL_DC_STEELWALL_2_HORIZONTAL;
6709       break;
6710
6711     case 0x16a9:        // steel wall 2 (up/down)
6712       element = EL_DC_STEELWALL_2_VERTICAL;
6713       break;
6714
6715     case 0x16aa:        // steel wall 2 (mid)
6716       element = EL_DC_STEELWALL_2_MIDDLE;
6717       break;
6718
6719     case 0x16ab:
6720       element = EL_SIGN_EXCLAMATION;
6721       break;
6722
6723     case 0x16ac:
6724       element = EL_SIGN_RADIOACTIVITY;
6725       break;
6726
6727     case 0x16ad:
6728       element = EL_SIGN_STOP;
6729       break;
6730
6731     case 0x16ae:
6732       element = EL_SIGN_WHEELCHAIR;
6733       break;
6734
6735     case 0x16af:
6736       element = EL_SIGN_PARKING;
6737       break;
6738
6739     case 0x16b0:
6740       element = EL_SIGN_NO_ENTRY;
6741       break;
6742
6743     case 0x16b1:
6744       element = EL_SIGN_HEART;
6745       break;
6746
6747     case 0x16b2:
6748       element = EL_SIGN_GIVE_WAY;
6749       break;
6750
6751     case 0x16b3:
6752       element = EL_SIGN_ENTRY_FORBIDDEN;
6753       break;
6754
6755     case 0x16b4:
6756       element = EL_SIGN_EMERGENCY_EXIT;
6757       break;
6758
6759     case 0x16b5:
6760       element = EL_SIGN_YIN_YANG;
6761       break;
6762
6763     case 0x16b6:
6764       element = EL_WALL_EMERALD;
6765       break;
6766
6767     case 0x16b7:
6768       element = EL_WALL_DIAMOND;
6769       break;
6770
6771     case 0x16b8:
6772       element = EL_WALL_PEARL;
6773       break;
6774
6775     case 0x16b9:
6776       element = EL_WALL_CRYSTAL;
6777       break;
6778
6779     case 0x16ba:
6780       element = EL_INVISIBLE_WALL;
6781       break;
6782
6783     case 0x16bb:
6784       element = EL_INVISIBLE_STEELWALL;
6785       break;
6786
6787       // 0x16bc - 0x16cb:
6788       // EL_INVISIBLE_SAND
6789
6790     case 0x16cc:
6791       element = EL_LIGHT_SWITCH;
6792       break;
6793
6794     case 0x16cd:
6795       element = EL_ENVELOPE_1;
6796       break;
6797
6798     default:
6799       if (element >= 0x0117 && element <= 0x036e)       // (?)
6800         element = EL_DIAMOND;
6801       else if (element >= 0x042d && element <= 0x0684)  // (?)
6802         element = EL_EMERALD;
6803       else if (element >= 0x157c && element <= 0x158b)
6804         element = EL_SAND;
6805       else if (element >= 0x1590 && element <= 0x159f)
6806         element = EL_DC_LANDMINE;
6807       else if (element >= 0x16bc && element <= 0x16cb)
6808         element = EL_INVISIBLE_SAND;
6809       else
6810       {
6811         Warn("unknown Diamond Caves element 0x%04x", element);
6812
6813         element = EL_UNKNOWN;
6814       }
6815       break;
6816   }
6817
6818   return getMappedElement(element);
6819 }
6820
6821 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6822 {
6823   byte header[DC_LEVEL_HEADER_SIZE];
6824   int envelope_size;
6825   int envelope_header_pos = 62;
6826   int envelope_content_pos = 94;
6827   int level_name_pos = 251;
6828   int level_author_pos = 292;
6829   int envelope_header_len;
6830   int envelope_content_len;
6831   int level_name_len;
6832   int level_author_len;
6833   int fieldx, fieldy;
6834   int num_yamyam_contents;
6835   int i, x, y;
6836
6837   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6838
6839   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6840   {
6841     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6842
6843     header[i * 2 + 0] = header_word >> 8;
6844     header[i * 2 + 1] = header_word & 0xff;
6845   }
6846
6847   // read some values from level header to check level decoding integrity
6848   fieldx = header[6] | (header[7] << 8);
6849   fieldy = header[8] | (header[9] << 8);
6850   num_yamyam_contents = header[60] | (header[61] << 8);
6851
6852   // do some simple sanity checks to ensure that level was correctly decoded
6853   if (fieldx < 1 || fieldx > 256 ||
6854       fieldy < 1 || fieldy > 256 ||
6855       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6856   {
6857     level->no_valid_file = TRUE;
6858
6859     Warn("cannot decode level from stream -- using empty level");
6860
6861     return;
6862   }
6863
6864   // maximum envelope header size is 31 bytes
6865   envelope_header_len   = header[envelope_header_pos];
6866   // maximum envelope content size is 110 (156?) bytes
6867   envelope_content_len  = header[envelope_content_pos];
6868
6869   // maximum level title size is 40 bytes
6870   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6871   // maximum level author size is 30 (51?) bytes
6872   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6873
6874   envelope_size = 0;
6875
6876   for (i = 0; i < envelope_header_len; i++)
6877     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878       level->envelope[0].text[envelope_size++] =
6879         header[envelope_header_pos + 1 + i];
6880
6881   if (envelope_header_len > 0 && envelope_content_len > 0)
6882   {
6883     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6884       level->envelope[0].text[envelope_size++] = '\n';
6885     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6886       level->envelope[0].text[envelope_size++] = '\n';
6887   }
6888
6889   for (i = 0; i < envelope_content_len; i++)
6890     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6891       level->envelope[0].text[envelope_size++] =
6892         header[envelope_content_pos + 1 + i];
6893
6894   level->envelope[0].text[envelope_size] = '\0';
6895
6896   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6897   level->envelope[0].ysize = 10;
6898   level->envelope[0].autowrap = TRUE;
6899   level->envelope[0].centered = TRUE;
6900
6901   for (i = 0; i < level_name_len; i++)
6902     level->name[i] = header[level_name_pos + 1 + i];
6903   level->name[level_name_len] = '\0';
6904
6905   for (i = 0; i < level_author_len; i++)
6906     level->author[i] = header[level_author_pos + 1 + i];
6907   level->author[level_author_len] = '\0';
6908
6909   num_yamyam_contents = header[60] | (header[61] << 8);
6910   level->num_yamyam_contents =
6911     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6912
6913   for (i = 0; i < num_yamyam_contents; i++)
6914   {
6915     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6916     {
6917       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6918       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6919
6920       if (i < MAX_ELEMENT_CONTENTS)
6921         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6922     }
6923   }
6924
6925   fieldx = header[6] | (header[7] << 8);
6926   fieldy = header[8] | (header[9] << 8);
6927   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6928   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6929
6930   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6931   {
6932     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6933     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6934
6935     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6936       level->field[x][y] = getMappedElement_DC(element_dc);
6937   }
6938
6939   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941   level->field[x][y] = EL_PLAYER_1;
6942
6943   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6944   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6945   level->field[x][y] = EL_PLAYER_2;
6946
6947   level->gems_needed            = header[18] | (header[19] << 8);
6948
6949   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6950   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6951   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6952   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6953   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6954   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6955   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6956   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6957   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6958   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6959   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6960   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6961
6962   level->time                   = header[44] | (header[45] << 8);
6963
6964   level->amoeba_speed           = header[46] | (header[47] << 8);
6965   level->time_light             = header[48] | (header[49] << 8);
6966   level->time_timegate          = header[50] | (header[51] << 8);
6967   level->time_wheel             = header[52] | (header[53] << 8);
6968   level->time_magic_wall        = header[54] | (header[55] << 8);
6969   level->extra_time             = header[56] | (header[57] << 8);
6970   level->shield_normal_time     = header[58] | (header[59] << 8);
6971
6972   // shield and extra time elements do not have a score
6973   level->score[SC_SHIELD]       = 0;
6974   level->extra_time_score       = 0;
6975
6976   // set time for normal and deadly shields to the same value
6977   level->shield_deadly_time     = level->shield_normal_time;
6978
6979   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6980   // can slip down from flat walls, like normal walls and steel walls
6981   level->em_slippery_gems = TRUE;
6982
6983   // time score is counted for each 10 seconds left in Diamond Caves levels
6984   level->time_score_base = 10;
6985 }
6986
6987 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6988                                      struct LevelFileInfo *level_file_info,
6989                                      boolean level_info_only)
6990 {
6991   char *filename = level_file_info->filename;
6992   File *file;
6993   int num_magic_bytes = 8;
6994   char magic_bytes[num_magic_bytes + 1];
6995   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6996
6997   if (!(file = openFile(filename, MODE_READ)))
6998   {
6999     level->no_valid_file = TRUE;
7000
7001     if (!level_info_only)
7002       Warn("cannot read level '%s' -- using empty level", filename);
7003
7004     return;
7005   }
7006
7007   // fseek(file, 0x0000, SEEK_SET);
7008
7009   if (level_file_info->packed)
7010   {
7011     // read "magic bytes" from start of file
7012     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7013       magic_bytes[0] = '\0';
7014
7015     // check "magic bytes" for correct file format
7016     if (!strPrefix(magic_bytes, "DC2"))
7017     {
7018       level->no_valid_file = TRUE;
7019
7020       Warn("unknown DC level file '%s' -- using empty level", filename);
7021
7022       return;
7023     }
7024
7025     if (strPrefix(magic_bytes, "DC2Win95") ||
7026         strPrefix(magic_bytes, "DC2Win98"))
7027     {
7028       int position_first_level = 0x00fa;
7029       int extra_bytes = 4;
7030       int skip_bytes;
7031
7032       // advance file stream to first level inside the level package
7033       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7034
7035       // each block of level data is followed by block of non-level data
7036       num_levels_to_skip *= 2;
7037
7038       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7039       while (num_levels_to_skip >= 0)
7040       {
7041         // advance file stream to next level inside the level package
7042         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7043         {
7044           level->no_valid_file = TRUE;
7045
7046           Warn("cannot fseek in file '%s' -- using empty level", filename);
7047
7048           return;
7049         }
7050
7051         // skip apparently unused extra bytes following each level
7052         ReadUnusedBytesFromFile(file, extra_bytes);
7053
7054         // read size of next level in level package
7055         skip_bytes = getFile32BitLE(file);
7056
7057         num_levels_to_skip--;
7058       }
7059     }
7060     else
7061     {
7062       level->no_valid_file = TRUE;
7063
7064       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7065
7066       return;
7067     }
7068   }
7069
7070   LoadLevelFromFileStream_DC(file, level);
7071
7072   closeFile(file);
7073 }
7074
7075
7076 // ----------------------------------------------------------------------------
7077 // functions for loading SB level
7078 // ----------------------------------------------------------------------------
7079
7080 int getMappedElement_SB(int element_ascii, boolean use_ces)
7081 {
7082   static struct
7083   {
7084     int ascii;
7085     int sb;
7086     int ce;
7087   }
7088   sb_element_mapping[] =
7089   {
7090     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7091     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7092     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7093     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7094     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7095     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7096     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7097     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7098
7099     { 0,   -1,                      -1          },
7100   };
7101
7102   int i;
7103
7104   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7105     if (element_ascii == sb_element_mapping[i].ascii)
7106       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7107
7108   return EL_UNDEFINED;
7109 }
7110
7111 static void SetLevelSettings_SB(struct LevelInfo *level)
7112 {
7113   // time settings
7114   level->time = 0;
7115   level->use_step_counter = TRUE;
7116
7117   // score settings
7118   level->score[SC_TIME_BONUS] = 0;
7119   level->time_score_base = 1;
7120   level->rate_time_over_score = TRUE;
7121
7122   // game settings
7123   level->auto_exit_sokoban = TRUE;
7124 }
7125
7126 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7127                                      struct LevelFileInfo *level_file_info,
7128                                      boolean level_info_only)
7129 {
7130   char *filename = level_file_info->filename;
7131   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7132   char last_comment[MAX_LINE_LEN];
7133   char level_name[MAX_LINE_LEN];
7134   char *line_ptr;
7135   File *file;
7136   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7137   boolean read_continued_line = FALSE;
7138   boolean reading_playfield = FALSE;
7139   boolean got_valid_playfield_line = FALSE;
7140   boolean invalid_playfield_char = FALSE;
7141   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7142   int file_level_nr = 0;
7143   int x = 0, y = 0;             // initialized to make compilers happy
7144
7145   last_comment[0] = '\0';
7146   level_name[0] = '\0';
7147
7148   if (!(file = openFile(filename, MODE_READ)))
7149   {
7150     level->no_valid_file = TRUE;
7151
7152     if (!level_info_only)
7153       Warn("cannot read level '%s' -- using empty level", filename);
7154
7155     return;
7156   }
7157
7158   while (!checkEndOfFile(file))
7159   {
7160     // level successfully read, but next level may follow here
7161     if (!got_valid_playfield_line && reading_playfield)
7162     {
7163       // read playfield from single level file -- skip remaining file
7164       if (!level_file_info->packed)
7165         break;
7166
7167       if (file_level_nr >= num_levels_to_skip)
7168         break;
7169
7170       file_level_nr++;
7171
7172       last_comment[0] = '\0';
7173       level_name[0] = '\0';
7174
7175       reading_playfield = FALSE;
7176     }
7177
7178     got_valid_playfield_line = FALSE;
7179
7180     // read next line of input file
7181     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7182       break;
7183
7184     // cut trailing line break (this can be newline and/or carriage return)
7185     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7186       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7187         *line_ptr = '\0';
7188
7189     // copy raw input line for later use (mainly debugging output)
7190     strcpy(line_raw, line);
7191
7192     if (read_continued_line)
7193     {
7194       // append new line to existing line, if there is enough space
7195       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7196         strcat(previous_line, line_ptr);
7197
7198       strcpy(line, previous_line);      // copy storage buffer to line
7199
7200       read_continued_line = FALSE;
7201     }
7202
7203     // if the last character is '\', continue at next line
7204     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7205     {
7206       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7207       strcpy(previous_line, line);      // copy line to storage buffer
7208
7209       read_continued_line = TRUE;
7210
7211       continue;
7212     }
7213
7214     // skip empty lines
7215     if (line[0] == '\0')
7216       continue;
7217
7218     // extract comment text from comment line
7219     if (line[0] == ';')
7220     {
7221       for (line_ptr = line; *line_ptr; line_ptr++)
7222         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7223           break;
7224
7225       strcpy(last_comment, line_ptr);
7226
7227       continue;
7228     }
7229
7230     // extract level title text from line containing level title
7231     if (line[0] == '\'')
7232     {
7233       strcpy(level_name, &line[1]);
7234
7235       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7236         level_name[strlen(level_name) - 1] = '\0';
7237
7238       continue;
7239     }
7240
7241     // skip lines containing only spaces (or empty lines)
7242     for (line_ptr = line; *line_ptr; line_ptr++)
7243       if (*line_ptr != ' ')
7244         break;
7245     if (*line_ptr == '\0')
7246       continue;
7247
7248     // at this point, we have found a line containing part of a playfield
7249
7250     got_valid_playfield_line = TRUE;
7251
7252     if (!reading_playfield)
7253     {
7254       reading_playfield = TRUE;
7255       invalid_playfield_char = FALSE;
7256
7257       for (x = 0; x < MAX_LEV_FIELDX; x++)
7258         for (y = 0; y < MAX_LEV_FIELDY; y++)
7259           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7260
7261       level->fieldx = 0;
7262       level->fieldy = 0;
7263
7264       // start with topmost tile row
7265       y = 0;
7266     }
7267
7268     // skip playfield line if larger row than allowed
7269     if (y >= MAX_LEV_FIELDY)
7270       continue;
7271
7272     // start with leftmost tile column
7273     x = 0;
7274
7275     // read playfield elements from line
7276     for (line_ptr = line; *line_ptr; line_ptr++)
7277     {
7278       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7279
7280       // stop parsing playfield line if larger column than allowed
7281       if (x >= MAX_LEV_FIELDX)
7282         break;
7283
7284       if (mapped_sb_element == EL_UNDEFINED)
7285       {
7286         invalid_playfield_char = TRUE;
7287
7288         break;
7289       }
7290
7291       level->field[x][y] = mapped_sb_element;
7292
7293       // continue with next tile column
7294       x++;
7295
7296       level->fieldx = MAX(x, level->fieldx);
7297     }
7298
7299     if (invalid_playfield_char)
7300     {
7301       // if first playfield line, treat invalid lines as comment lines
7302       if (y == 0)
7303         reading_playfield = FALSE;
7304
7305       continue;
7306     }
7307
7308     // continue with next tile row
7309     y++;
7310   }
7311
7312   closeFile(file);
7313
7314   level->fieldy = y;
7315
7316   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7317   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7318
7319   if (!reading_playfield)
7320   {
7321     level->no_valid_file = TRUE;
7322
7323     Warn("cannot read level '%s' -- using empty level", filename);
7324
7325     return;
7326   }
7327
7328   if (*level_name != '\0')
7329   {
7330     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7331     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7332   }
7333   else if (*last_comment != '\0')
7334   {
7335     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7336     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7337   }
7338   else
7339   {
7340     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7341   }
7342
7343   // set all empty fields beyond the border walls to invisible steel wall
7344   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7345   {
7346     if ((x == 0 || x == level->fieldx - 1 ||
7347          y == 0 || y == level->fieldy - 1) &&
7348         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7349       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7350                      level->field, level->fieldx, level->fieldy);
7351   }
7352
7353   // set special level settings for Sokoban levels
7354   SetLevelSettings_SB(level);
7355
7356   if (load_xsb_to_ces)
7357   {
7358     // special global settings can now be set in level template
7359     level->use_custom_template = TRUE;
7360   }
7361 }
7362
7363
7364 // -------------------------------------------------------------------------
7365 // functions for handling native levels
7366 // -------------------------------------------------------------------------
7367
7368 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7369                                      struct LevelFileInfo *level_file_info,
7370                                      boolean level_info_only)
7371 {
7372   int pos = 0;
7373
7374   // determine position of requested level inside level package
7375   if (level_file_info->packed)
7376     pos = level_file_info->nr - leveldir_current->first_level;
7377
7378   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7379     level->no_valid_file = TRUE;
7380 }
7381
7382 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7383                                      struct LevelFileInfo *level_file_info,
7384                                      boolean level_info_only)
7385 {
7386   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7387     level->no_valid_file = TRUE;
7388 }
7389
7390 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7391                                      struct LevelFileInfo *level_file_info,
7392                                      boolean level_info_only)
7393 {
7394   int pos = 0;
7395
7396   // determine position of requested level inside level package
7397   if (level_file_info->packed)
7398     pos = level_file_info->nr - leveldir_current->first_level;
7399
7400   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7401     level->no_valid_file = TRUE;
7402 }
7403
7404 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7405                                      struct LevelFileInfo *level_file_info,
7406                                      boolean level_info_only)
7407 {
7408   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7409     level->no_valid_file = TRUE;
7410 }
7411
7412 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7413 {
7414   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7415     CopyNativeLevel_RND_to_BD(level);
7416   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7417     CopyNativeLevel_RND_to_EM(level);
7418   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7419     CopyNativeLevel_RND_to_SP(level);
7420   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7421     CopyNativeLevel_RND_to_MM(level);
7422 }
7423
7424 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7425 {
7426   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7427     CopyNativeLevel_BD_to_RND(level);
7428   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7429     CopyNativeLevel_EM_to_RND(level);
7430   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7431     CopyNativeLevel_SP_to_RND(level);
7432   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7433     CopyNativeLevel_MM_to_RND(level);
7434 }
7435
7436 void SaveNativeLevel(struct LevelInfo *level)
7437 {
7438   // saving native level files only supported for some game engines
7439   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7440       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7441     return;
7442
7443   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7444                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7445   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7446   char *filename = getLevelFilenameFromBasename(basename);
7447
7448   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7449     return;
7450
7451   boolean success = FALSE;
7452
7453   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7454   {
7455     CopyNativeLevel_RND_to_BD(level);
7456     // CopyNativeTape_RND_to_BD(level);
7457
7458     success = SaveNativeLevel_BD(filename);
7459   }
7460   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7461   {
7462     CopyNativeLevel_RND_to_SP(level);
7463     CopyNativeTape_RND_to_SP(level);
7464
7465     success = SaveNativeLevel_SP(filename);
7466   }
7467
7468   if (success)
7469     Request("Native level file saved!", REQ_CONFIRM);
7470   else
7471     Request("Failed to save native level file!", REQ_CONFIRM);
7472 }
7473
7474
7475 // ----------------------------------------------------------------------------
7476 // functions for loading generic level
7477 // ----------------------------------------------------------------------------
7478
7479 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7480                                   struct LevelFileInfo *level_file_info,
7481                                   boolean level_info_only)
7482 {
7483   // always start with reliable default values
7484   setLevelInfoToDefaults(level, level_info_only, TRUE);
7485
7486   switch (level_file_info->type)
7487   {
7488     case LEVEL_FILE_TYPE_RND:
7489       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7490       break;
7491
7492     case LEVEL_FILE_TYPE_BD:
7493       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7494       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7495       break;
7496
7497     case LEVEL_FILE_TYPE_EM:
7498       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7499       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7500       break;
7501
7502     case LEVEL_FILE_TYPE_SP:
7503       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7504       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7505       break;
7506
7507     case LEVEL_FILE_TYPE_MM:
7508       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7509       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7510       break;
7511
7512     case LEVEL_FILE_TYPE_DC:
7513       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7514       break;
7515
7516     case LEVEL_FILE_TYPE_SB:
7517       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7518       break;
7519
7520     default:
7521       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7522       break;
7523   }
7524
7525   // if level file is invalid, restore level structure to default values
7526   if (level->no_valid_file)
7527     setLevelInfoToDefaults(level, level_info_only, FALSE);
7528
7529   if (check_special_flags("use_native_bd_game_engine"))
7530     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7531
7532   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7533     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7534
7535   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7536     CopyNativeLevel_Native_to_RND(level);
7537 }
7538
7539 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7540 {
7541   static struct LevelFileInfo level_file_info;
7542
7543   // always start with reliable default values
7544   setFileInfoToDefaults(&level_file_info);
7545
7546   level_file_info.nr = 0;                       // unknown level number
7547   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7548
7549   setString(&level_file_info.filename, filename);
7550
7551   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7552 }
7553
7554 static void LoadLevel_FixEnvelopes(struct LevelInfo *level, boolean skip_single_lines)
7555 {
7556   // This function removes newlines in envelopes after lines of text ending in the last column
7557   // of the envelope. In earlier versions, these newlines were removed when displaying envelopes,
7558   // but caused trouble in the level editor. In version 4.3.2.3, this problem was partially
7559   // fixed in the level editor (but only for single full-width text lines followed by a newline,
7560   // not for multiple lines ending in the last column, followed by a newline), but now produced
7561   // unwanted newlines in the game for envelopes stored by previous game versions, which was not
7562   // intended by the level author (and sometimes caused text lines not being displayed anymore at
7563   // the bottom of the envelope).
7564   //
7565   // This function should solve these problems by removing such newline characters from envelopes
7566   // stored by older game versions.
7567
7568   int envelope_nr;
7569
7570   for (envelope_nr = 0; envelope_nr < NUM_ENVELOPES; envelope_nr++)
7571   {
7572     char *envelope_ptr = level->envelope[envelope_nr].text;
7573     int envelope_xsize = level->envelope[envelope_nr].xsize;
7574     int envelope_size = strlen(envelope_ptr);
7575     int start = 0;
7576     int i;
7577
7578     for (i = 0; i < envelope_size; i++)
7579     {
7580       // check for newlines in envelope
7581       if (envelope_ptr[i] == '\n')
7582       {
7583         int line_length = i - start;
7584
7585         // check for (non-empty) lines that are a multiple of the envelope width,
7586         // causing a line break inside the envelope (text area in editor and in game)
7587         if (line_length > 0 && line_length % envelope_xsize == 0)
7588         {
7589           // special case: skip fixing single lines for newer versions
7590           boolean skip_fixing_line = (line_length == 1 && skip_single_lines);
7591
7592           if (!skip_fixing_line)
7593           {
7594             int j;
7595
7596             // remove newline character from string
7597             for (j = i; j < envelope_size; j++)
7598               envelope_ptr[j] = envelope_ptr[j + 1];
7599           }
7600
7601           // continue with next line (that was copied over the newline)
7602           start = i;
7603         }
7604         else
7605         {
7606           // continue with next character after newline
7607           start = i + 1;
7608         }
7609       }
7610     }
7611   }
7612 }
7613
7614 static void LoadLevel_InitVersion(struct LevelInfo *level)
7615 {
7616   int i, j;
7617
7618   if (leveldir_current == NULL)         // only when dumping level
7619     return;
7620
7621   // all engine modifications also valid for levels which use latest engine
7622   if (level->game_version < VERSION_IDENT(3,2,0,5))
7623   {
7624     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7625     level->time_score_base = 10;
7626   }
7627
7628   if (leveldir_current->latest_engine)
7629   {
7630     // ---------- use latest game engine --------------------------------------
7631
7632     /* For all levels which are forced to use the latest game engine version
7633        (normally all but user contributed, private and undefined levels), set
7634        the game engine version to the actual version; this allows for actual
7635        corrections in the game engine to take effect for existing, converted
7636        levels (from "classic" or other existing games) to make the emulation
7637        of the corresponding game more accurate, while (hopefully) not breaking
7638        existing levels created from other players. */
7639
7640     level->game_version = GAME_VERSION_ACTUAL;
7641
7642     /* Set special EM style gems behaviour: EM style gems slip down from
7643        normal, steel and growing wall. As this is a more fundamental change,
7644        it seems better to set the default behaviour to "off" (as it is more
7645        natural) and make it configurable in the level editor (as a property
7646        of gem style elements). Already existing converted levels (neither
7647        private nor contributed levels) are changed to the new behaviour. */
7648
7649     if (level->file_version < FILE_VERSION_2_0)
7650       level->em_slippery_gems = TRUE;
7651
7652     return;
7653   }
7654
7655   // ---------- use game engine the level was created with --------------------
7656
7657   /* For all levels which are not forced to use the latest game engine
7658      version (normally user contributed, private and undefined levels),
7659      use the version of the game engine the levels were created for.
7660
7661      Since 2.0.1, the game engine version is now directly stored
7662      in the level file (chunk "VERS"), so there is no need anymore
7663      to set the game version from the file version (except for old,
7664      pre-2.0 levels, where the game version is still taken from the
7665      file format version used to store the level -- see above). */
7666
7667   // player was faster than enemies in 1.0.0 and before
7668   if (level->file_version == FILE_VERSION_1_0)
7669     for (i = 0; i < MAX_PLAYERS; i++)
7670       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7671
7672   // default behaviour for EM style gems was "slippery" only in 2.0.1
7673   if (level->game_version == VERSION_IDENT(2,0,1,0))
7674     level->em_slippery_gems = TRUE;
7675
7676   // springs could be pushed over pits before (pre-release version) 2.2.0
7677   if (level->game_version < VERSION_IDENT(2,2,0,0))
7678     level->use_spring_bug = TRUE;
7679
7680   if (level->game_version < VERSION_IDENT(3,2,0,5))
7681   {
7682     // time orb caused limited time in endless time levels before 3.2.0-5
7683     level->use_time_orb_bug = TRUE;
7684
7685     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7686     level->block_snap_field = FALSE;
7687
7688     // extra time score was same value as time left score before 3.2.0-5
7689     level->extra_time_score = level->score[SC_TIME_BONUS];
7690   }
7691
7692   if (level->game_version < VERSION_IDENT(3,2,0,7))
7693   {
7694     // default behaviour for snapping was "not continuous" before 3.2.0-7
7695     level->continuous_snapping = FALSE;
7696   }
7697
7698   // only few elements were able to actively move into acid before 3.1.0
7699   // trigger settings did not exist before 3.1.0; set to default "any"
7700   if (level->game_version < VERSION_IDENT(3,1,0,0))
7701   {
7702     // correct "can move into acid" settings (all zero in old levels)
7703
7704     level->can_move_into_acid_bits = 0; // nothing can move into acid
7705     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7706
7707     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7708     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7709     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7710     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7711
7712     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7713       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7714
7715     // correct trigger settings (stored as zero == "none" in old levels)
7716
7717     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7718     {
7719       int element = EL_CUSTOM_START + i;
7720       struct ElementInfo *ei = &element_info[element];
7721
7722       for (j = 0; j < ei->num_change_pages; j++)
7723       {
7724         struct ElementChangeInfo *change = &ei->change_page[j];
7725
7726         change->trigger_player = CH_PLAYER_ANY;
7727         change->trigger_page = CH_PAGE_ANY;
7728       }
7729     }
7730   }
7731
7732   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7733   {
7734     int element = EL_CUSTOM_256;
7735     struct ElementInfo *ei = &element_info[element];
7736     struct ElementChangeInfo *change = &ei->change_page[0];
7737
7738     /* This is needed to fix a problem that was caused by a bugfix in function
7739        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7740        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7741        not replace walkable elements, but instead just placed the player on it,
7742        without placing the Sokoban field under the player). Unfortunately, this
7743        breaks "Snake Bite" style levels when the snake is halfway through a door
7744        that just closes (the snake head is still alive and can be moved in this
7745        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7746        player (without Sokoban element) which then gets killed as designed). */
7747
7748     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7749          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7750         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7751       change->target_element = EL_PLAYER_1;
7752   }
7753
7754   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7755   if (level->game_version < VERSION_IDENT(3,2,5,0))
7756   {
7757     /* This is needed to fix a problem that was caused by a bugfix in function
7758        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7759        corrects the behaviour when a custom element changes to another custom
7760        element with a higher element number that has change actions defined.
7761        Normally, only one change per frame is allowed for custom elements.
7762        Therefore, it is checked if a custom element already changed in the
7763        current frame; if it did, subsequent changes are suppressed.
7764        Unfortunately, this is only checked for element changes, but not for
7765        change actions, which are still executed. As the function above loops
7766        through all custom elements from lower to higher, an element change
7767        resulting in a lower CE number won't be checked again, while a target
7768        element with a higher number will also be checked, and potential change
7769        actions will get executed for this CE, too (which is wrong), while
7770        further changes are ignored (which is correct). As this bugfix breaks
7771        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7772        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7773        behaviour for existing levels and tapes that make use of this bug */
7774
7775     level->use_action_after_change_bug = TRUE;
7776   }
7777
7778   // not centering level after relocating player was default only in 3.2.3
7779   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7780     level->shifted_relocation = TRUE;
7781
7782   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7783   if (level->game_version < VERSION_IDENT(3,2,6,0))
7784     level->em_explodes_by_fire = TRUE;
7785
7786   // levels were solved by the first player entering an exit up to 4.1.0.0
7787   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7788     level->solved_by_one_player = TRUE;
7789
7790   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7791   if (level->game_version < VERSION_IDENT(4,1,1,1))
7792     level->use_life_bugs = TRUE;
7793
7794   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7795   if (level->game_version < VERSION_IDENT(4,1,1,1))
7796     level->sb_objects_needed = FALSE;
7797
7798   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7799   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7800     level->finish_dig_collect = FALSE;
7801
7802   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7803   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7804     level->keep_walkable_ce = TRUE;
7805
7806   // envelopes may contain broken or too many line breaks before 4.4.0.0
7807   if (level->game_version < VERSION_IDENT(4,4,0,0))
7808     LoadLevel_FixEnvelopes(level, (level->game_version >= VERSION_IDENT(4,3,2,3)));
7809 }
7810
7811 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7812 {
7813   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7814   int x, y;
7815
7816   // check if this level is (not) a Sokoban level
7817   for (y = 0; y < level->fieldy; y++)
7818     for (x = 0; x < level->fieldx; x++)
7819       if (!IS_SB_ELEMENT(Tile[x][y]))
7820         is_sokoban_level = FALSE;
7821
7822   if (is_sokoban_level)
7823   {
7824     // set special level settings for Sokoban levels
7825     SetLevelSettings_SB(level);
7826   }
7827 }
7828
7829 static void LoadLevel_InitSettings(struct LevelInfo *level)
7830 {
7831   // adjust level settings for (non-native) Sokoban-style levels
7832   LoadLevel_InitSettings_SB(level);
7833
7834   // rename levels with title "nameless level" or if renaming is forced
7835   if (leveldir_current->empty_level_name != NULL &&
7836       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7837        leveldir_current->force_level_name))
7838     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7839              leveldir_current->empty_level_name, level_nr);
7840 }
7841
7842 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7843 {
7844   int i, x, y;
7845
7846   // map elements that have changed in newer versions
7847   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7848                                                     level->game_version);
7849   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7850     for (x = 0; x < 3; x++)
7851       for (y = 0; y < 3; y++)
7852         level->yamyam_content[i].e[x][y] =
7853           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7854                                     level->game_version);
7855
7856 }
7857
7858 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7859 {
7860   int i, j;
7861
7862   // map custom element change events that have changed in newer versions
7863   // (these following values were accidentally changed in version 3.0.1)
7864   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7865   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7866   {
7867     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7868     {
7869       int element = EL_CUSTOM_START + i;
7870
7871       // order of checking and copying events to be mapped is important
7872       // (do not change the start and end value -- they are constant)
7873       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7874       {
7875         if (HAS_CHANGE_EVENT(element, j - 2))
7876         {
7877           SET_CHANGE_EVENT(element, j - 2, FALSE);
7878           SET_CHANGE_EVENT(element, j, TRUE);
7879         }
7880       }
7881
7882       // order of checking and copying events to be mapped is important
7883       // (do not change the start and end value -- they are constant)
7884       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7885       {
7886         if (HAS_CHANGE_EVENT(element, j - 1))
7887         {
7888           SET_CHANGE_EVENT(element, j - 1, FALSE);
7889           SET_CHANGE_EVENT(element, j, TRUE);
7890         }
7891       }
7892     }
7893   }
7894
7895   // initialize "can_change" field for old levels with only one change page
7896   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7897   {
7898     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7899     {
7900       int element = EL_CUSTOM_START + i;
7901
7902       if (CAN_CHANGE(element))
7903         element_info[element].change->can_change = TRUE;
7904     }
7905   }
7906
7907   // correct custom element values (for old levels without these options)
7908   if (level->game_version < VERSION_IDENT(3,1,1,0))
7909   {
7910     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7911     {
7912       int element = EL_CUSTOM_START + i;
7913       struct ElementInfo *ei = &element_info[element];
7914
7915       if (ei->access_direction == MV_NO_DIRECTION)
7916         ei->access_direction = MV_ALL_DIRECTIONS;
7917     }
7918   }
7919
7920   // correct custom element values (fix invalid values for all versions)
7921   if (1)
7922   {
7923     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7924     {
7925       int element = EL_CUSTOM_START + i;
7926       struct ElementInfo *ei = &element_info[element];
7927
7928       for (j = 0; j < ei->num_change_pages; j++)
7929       {
7930         struct ElementChangeInfo *change = &ei->change_page[j];
7931
7932         if (change->trigger_player == CH_PLAYER_NONE)
7933           change->trigger_player = CH_PLAYER_ANY;
7934
7935         if (change->trigger_side == CH_SIDE_NONE)
7936           change->trigger_side = CH_SIDE_ANY;
7937       }
7938     }
7939   }
7940
7941   // initialize "can_explode" field for old levels which did not store this
7942   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7943   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7944   {
7945     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7946     {
7947       int element = EL_CUSTOM_START + i;
7948
7949       if (EXPLODES_1X1_OLD(element))
7950         element_info[element].explosion_type = EXPLODES_1X1;
7951
7952       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7953                                              EXPLODES_SMASHED(element) ||
7954                                              EXPLODES_IMPACT(element)));
7955     }
7956   }
7957
7958   // correct previously hard-coded move delay values for maze runner style
7959   if (level->game_version < VERSION_IDENT(3,1,1,0))
7960   {
7961     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7962     {
7963       int element = EL_CUSTOM_START + i;
7964
7965       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7966       {
7967         // previously hard-coded and therefore ignored
7968         element_info[element].move_delay_fixed = 9;
7969         element_info[element].move_delay_random = 0;
7970       }
7971     }
7972   }
7973
7974   // set some other uninitialized values of custom elements in older levels
7975   if (level->game_version < VERSION_IDENT(3,1,0,0))
7976   {
7977     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7978     {
7979       int element = EL_CUSTOM_START + i;
7980
7981       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7982
7983       element_info[element].explosion_delay = 17;
7984       element_info[element].ignition_delay = 8;
7985     }
7986   }
7987
7988   // set mouse click change events to work for left/middle/right mouse button
7989   if (level->game_version < VERSION_IDENT(4,2,3,0))
7990   {
7991     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7992     {
7993       int element = EL_CUSTOM_START + i;
7994       struct ElementInfo *ei = &element_info[element];
7995
7996       for (j = 0; j < ei->num_change_pages; j++)
7997       {
7998         struct ElementChangeInfo *change = &ei->change_page[j];
7999
8000         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
8001             change->has_event[CE_PRESSED_BY_MOUSE] ||
8002             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
8003             change->has_event[CE_MOUSE_PRESSED_ON_X])
8004           change->trigger_side = CH_SIDE_ANY;
8005       }
8006     }
8007   }
8008 }
8009
8010 static void LoadLevel_InitElements(struct LevelInfo *level)
8011 {
8012   LoadLevel_InitStandardElements(level);
8013
8014   if (level->file_has_custom_elements)
8015     LoadLevel_InitCustomElements(level);
8016
8017   // initialize element properties for level editor etc.
8018   InitElementPropertiesEngine(level->game_version);
8019   InitElementPropertiesGfxElement();
8020 }
8021
8022 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
8023 {
8024   int x, y;
8025
8026   // map elements that have changed in newer versions
8027   for (y = 0; y < level->fieldy; y++)
8028     for (x = 0; x < level->fieldx; x++)
8029       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
8030                                                      level->game_version);
8031
8032   // clear unused playfield data (nicer if level gets resized in editor)
8033   for (x = 0; x < MAX_LEV_FIELDX; x++)
8034     for (y = 0; y < MAX_LEV_FIELDY; y++)
8035       if (x >= level->fieldx || y >= level->fieldy)
8036         level->field[x][y] = EL_EMPTY;
8037
8038   // copy elements to runtime playfield array
8039   for (x = 0; x < MAX_LEV_FIELDX; x++)
8040     for (y = 0; y < MAX_LEV_FIELDY; y++)
8041       Tile[x][y] = level->field[x][y];
8042
8043   // initialize level size variables for faster access
8044   lev_fieldx = level->fieldx;
8045   lev_fieldy = level->fieldy;
8046
8047   // determine border element for this level
8048   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
8049     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
8050   else
8051     SetBorderElement();
8052 }
8053
8054 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
8055 {
8056   struct LevelFileInfo *level_file_info = &level->file_info;
8057
8058   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
8059     CopyNativeLevel_RND_to_Native(level);
8060 }
8061
8062 static void LoadLevelTemplate_LoadAndInit(void)
8063 {
8064   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8065
8066   LoadLevel_InitVersion(&level_template);
8067   LoadLevel_InitElements(&level_template);
8068   LoadLevel_InitSettings(&level_template);
8069
8070   ActivateLevelTemplate();
8071 }
8072
8073 void LoadLevelTemplate(int nr)
8074 {
8075   if (!fileExists(getGlobalLevelTemplateFilename()))
8076   {
8077     Warn("no level template found for this level");
8078
8079     return;
8080   }
8081
8082   setLevelFileInfo(&level_template.file_info, nr);
8083
8084   LoadLevelTemplate_LoadAndInit();
8085 }
8086
8087 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8088 {
8089   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8090
8091   LoadLevelTemplate_LoadAndInit();
8092 }
8093
8094 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8095 {
8096   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8097
8098   if (level.use_custom_template)
8099   {
8100     if (network_level != NULL)
8101       LoadNetworkLevelTemplate(network_level);
8102     else
8103       LoadLevelTemplate(-1);
8104   }
8105
8106   LoadLevel_InitVersion(&level);
8107   LoadLevel_InitElements(&level);
8108   LoadLevel_InitPlayfield(&level);
8109   LoadLevel_InitSettings(&level);
8110
8111   LoadLevel_InitNativeEngines(&level);
8112 }
8113
8114 void LoadLevel(int nr)
8115 {
8116   SetLevelSetInfo(leveldir_current->identifier, nr);
8117
8118   setLevelFileInfo(&level.file_info, nr);
8119
8120   LoadLevel_LoadAndInit(NULL);
8121 }
8122
8123 void LoadLevelInfoOnly(int nr)
8124 {
8125   setLevelFileInfo(&level.file_info, nr);
8126
8127   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8128 }
8129
8130 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8131 {
8132   SetLevelSetInfo(network_level->leveldir_identifier,
8133                   network_level->file_info.nr);
8134
8135   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8136
8137   LoadLevel_LoadAndInit(network_level);
8138 }
8139
8140 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8141 {
8142   int chunk_size = 0;
8143
8144   chunk_size += putFileVersion(file, level->file_version);
8145   chunk_size += putFileVersion(file, level->game_version);
8146
8147   return chunk_size;
8148 }
8149
8150 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8151 {
8152   int chunk_size = 0;
8153
8154   chunk_size += putFile16BitBE(file, level->creation_date.year);
8155   chunk_size += putFile8Bit(file,    level->creation_date.month);
8156   chunk_size += putFile8Bit(file,    level->creation_date.day);
8157
8158   return chunk_size;
8159 }
8160
8161 #if ENABLE_HISTORIC_CHUNKS
8162 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8163 {
8164   int i, x, y;
8165
8166   putFile8Bit(file, level->fieldx);
8167   putFile8Bit(file, level->fieldy);
8168
8169   putFile16BitBE(file, level->time);
8170   putFile16BitBE(file, level->gems_needed);
8171
8172   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8173     putFile8Bit(file, level->name[i]);
8174
8175   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8176     putFile8Bit(file, level->score[i]);
8177
8178   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8179     for (y = 0; y < 3; y++)
8180       for (x = 0; x < 3; x++)
8181         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8182                            level->yamyam_content[i].e[x][y]));
8183   putFile8Bit(file, level->amoeba_speed);
8184   putFile8Bit(file, level->time_magic_wall);
8185   putFile8Bit(file, level->time_wheel);
8186   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8187                      level->amoeba_content));
8188   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8189   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8190   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8191   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8192
8193   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8194
8195   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8196   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8197   putFile32BitBE(file, level->can_move_into_acid_bits);
8198   putFile8Bit(file, level->dont_collide_with_bits);
8199
8200   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8201   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8202
8203   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8204   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8205   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8206
8207   putFile8Bit(file, level->game_engine_type);
8208
8209   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8210 }
8211 #endif
8212
8213 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8214 {
8215   int chunk_size = 0;
8216   int i;
8217
8218   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8219     chunk_size += putFile8Bit(file, level->name[i]);
8220
8221   return chunk_size;
8222 }
8223
8224 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8225 {
8226   int chunk_size = 0;
8227   int i;
8228
8229   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8230     chunk_size += putFile8Bit(file, level->author[i]);
8231
8232   return chunk_size;
8233 }
8234
8235 #if ENABLE_HISTORIC_CHUNKS
8236 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8237 {
8238   int chunk_size = 0;
8239   int x, y;
8240
8241   for (y = 0; y < level->fieldy; y++)
8242     for (x = 0; x < level->fieldx; x++)
8243       if (level->encoding_16bit_field)
8244         chunk_size += putFile16BitBE(file, level->field[x][y]);
8245       else
8246         chunk_size += putFile8Bit(file, level->field[x][y]);
8247
8248   return chunk_size;
8249 }
8250 #endif
8251
8252 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8253 {
8254   int chunk_size = 0;
8255   int x, y;
8256
8257   for (y = 0; y < level->fieldy; y++) 
8258     for (x = 0; x < level->fieldx; x++) 
8259       chunk_size += putFile16BitBE(file, level->field[x][y]);
8260
8261   return chunk_size;
8262 }
8263
8264 #if ENABLE_HISTORIC_CHUNKS
8265 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8266 {
8267   int i, x, y;
8268
8269   putFile8Bit(file, EL_YAMYAM);
8270   putFile8Bit(file, level->num_yamyam_contents);
8271   putFile8Bit(file, 0);
8272   putFile8Bit(file, 0);
8273
8274   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8275     for (y = 0; y < 3; y++)
8276       for (x = 0; x < 3; x++)
8277         if (level->encoding_16bit_field)
8278           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8279         else
8280           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8281 }
8282 #endif
8283
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8286 {
8287   int i, x, y;
8288   int num_contents, content_xsize, content_ysize;
8289   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8290
8291   if (element == EL_YAMYAM)
8292   {
8293     num_contents = level->num_yamyam_contents;
8294     content_xsize = 3;
8295     content_ysize = 3;
8296
8297     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8298       for (y = 0; y < 3; y++)
8299         for (x = 0; x < 3; x++)
8300           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8301   }
8302   else if (element == EL_BD_AMOEBA)
8303   {
8304     num_contents = 1;
8305     content_xsize = 1;
8306     content_ysize = 1;
8307
8308     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8309       for (y = 0; y < 3; y++)
8310         for (x = 0; x < 3; x++)
8311           content_array[i][x][y] = EL_EMPTY;
8312     content_array[0][0][0] = level->amoeba_content;
8313   }
8314   else
8315   {
8316     // chunk header already written -- write empty chunk data
8317     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8318
8319     Warn("cannot save content for element '%d'", element);
8320
8321     return;
8322   }
8323
8324   putFile16BitBE(file, element);
8325   putFile8Bit(file, num_contents);
8326   putFile8Bit(file, content_xsize);
8327   putFile8Bit(file, content_ysize);
8328
8329   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8330
8331   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8332     for (y = 0; y < 3; y++)
8333       for (x = 0; x < 3; x++)
8334         putFile16BitBE(file, content_array[i][x][y]);
8335 }
8336 #endif
8337
8338 #if ENABLE_HISTORIC_CHUNKS
8339 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8340 {
8341   int envelope_nr = element - EL_ENVELOPE_1;
8342   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8343   int chunk_size = 0;
8344   int i;
8345
8346   chunk_size += putFile16BitBE(file, element);
8347   chunk_size += putFile16BitBE(file, envelope_len);
8348   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8349   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8350
8351   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8352   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8353
8354   for (i = 0; i < envelope_len; i++)
8355     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8356
8357   return chunk_size;
8358 }
8359 #endif
8360
8361 #if ENABLE_HISTORIC_CHUNKS
8362 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8363                            int num_changed_custom_elements)
8364 {
8365   int i, check = 0;
8366
8367   putFile16BitBE(file, num_changed_custom_elements);
8368
8369   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8370   {
8371     int element = EL_CUSTOM_START + i;
8372
8373     struct ElementInfo *ei = &element_info[element];
8374
8375     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8376     {
8377       if (check < num_changed_custom_elements)
8378       {
8379         putFile16BitBE(file, element);
8380         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8381       }
8382
8383       check++;
8384     }
8385   }
8386
8387   if (check != num_changed_custom_elements)     // should not happen
8388     Warn("inconsistent number of custom element properties");
8389 }
8390 #endif
8391
8392 #if ENABLE_HISTORIC_CHUNKS
8393 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8394                            int num_changed_custom_elements)
8395 {
8396   int i, check = 0;
8397
8398   putFile16BitBE(file, num_changed_custom_elements);
8399
8400   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8401   {
8402     int element = EL_CUSTOM_START + i;
8403
8404     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8405     {
8406       if (check < num_changed_custom_elements)
8407       {
8408         putFile16BitBE(file, element);
8409         putFile16BitBE(file, element_info[element].change->target_element);
8410       }
8411
8412       check++;
8413     }
8414   }
8415
8416   if (check != num_changed_custom_elements)     // should not happen
8417     Warn("inconsistent number of custom target elements");
8418 }
8419 #endif
8420
8421 #if ENABLE_HISTORIC_CHUNKS
8422 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8423                            int num_changed_custom_elements)
8424 {
8425   int i, j, x, y, check = 0;
8426
8427   putFile16BitBE(file, num_changed_custom_elements);
8428
8429   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8430   {
8431     int element = EL_CUSTOM_START + i;
8432     struct ElementInfo *ei = &element_info[element];
8433
8434     if (ei->modified_settings)
8435     {
8436       if (check < num_changed_custom_elements)
8437       {
8438         putFile16BitBE(file, element);
8439
8440         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8441           putFile8Bit(file, ei->description[j]);
8442
8443         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8444
8445         // some free bytes for future properties and padding
8446         WriteUnusedBytesToFile(file, 7);
8447
8448         putFile8Bit(file, ei->use_gfx_element);
8449         putFile16BitBE(file, ei->gfx_element_initial);
8450
8451         putFile8Bit(file, ei->collect_score_initial);
8452         putFile8Bit(file, ei->collect_count_initial);
8453
8454         putFile16BitBE(file, ei->push_delay_fixed);
8455         putFile16BitBE(file, ei->push_delay_random);
8456         putFile16BitBE(file, ei->move_delay_fixed);
8457         putFile16BitBE(file, ei->move_delay_random);
8458
8459         putFile16BitBE(file, ei->move_pattern);
8460         putFile8Bit(file, ei->move_direction_initial);
8461         putFile8Bit(file, ei->move_stepsize);
8462
8463         for (y = 0; y < 3; y++)
8464           for (x = 0; x < 3; x++)
8465             putFile16BitBE(file, ei->content.e[x][y]);
8466
8467         putFile32BitBE(file, ei->change->events);
8468
8469         putFile16BitBE(file, ei->change->target_element);
8470
8471         putFile16BitBE(file, ei->change->delay_fixed);
8472         putFile16BitBE(file, ei->change->delay_random);
8473         putFile16BitBE(file, ei->change->delay_frames);
8474
8475         putFile16BitBE(file, ei->change->initial_trigger_element);
8476
8477         putFile8Bit(file, ei->change->explode);
8478         putFile8Bit(file, ei->change->use_target_content);
8479         putFile8Bit(file, ei->change->only_if_complete);
8480         putFile8Bit(file, ei->change->use_random_replace);
8481
8482         putFile8Bit(file, ei->change->random_percentage);
8483         putFile8Bit(file, ei->change->replace_when);
8484
8485         for (y = 0; y < 3; y++)
8486           for (x = 0; x < 3; x++)
8487             putFile16BitBE(file, ei->change->content.e[x][y]);
8488
8489         putFile8Bit(file, ei->slippery_type);
8490
8491         // some free bytes for future properties and padding
8492         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8493       }
8494
8495       check++;
8496     }
8497   }
8498
8499   if (check != num_changed_custom_elements)     // should not happen
8500     Warn("inconsistent number of custom element properties");
8501 }
8502 #endif
8503
8504 #if ENABLE_HISTORIC_CHUNKS
8505 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8506 {
8507   struct ElementInfo *ei = &element_info[element];
8508   int i, j, x, y;
8509
8510   // ---------- custom element base property values (96 bytes) ----------------
8511
8512   putFile16BitBE(file, element);
8513
8514   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8515     putFile8Bit(file, ei->description[i]);
8516
8517   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8518
8519   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8520
8521   putFile8Bit(file, ei->num_change_pages);
8522
8523   putFile16BitBE(file, ei->ce_value_fixed_initial);
8524   putFile16BitBE(file, ei->ce_value_random_initial);
8525   putFile8Bit(file, ei->use_last_ce_value);
8526
8527   putFile8Bit(file, ei->use_gfx_element);
8528   putFile16BitBE(file, ei->gfx_element_initial);
8529
8530   putFile8Bit(file, ei->collect_score_initial);
8531   putFile8Bit(file, ei->collect_count_initial);
8532
8533   putFile8Bit(file, ei->drop_delay_fixed);
8534   putFile8Bit(file, ei->push_delay_fixed);
8535   putFile8Bit(file, ei->drop_delay_random);
8536   putFile8Bit(file, ei->push_delay_random);
8537   putFile16BitBE(file, ei->move_delay_fixed);
8538   putFile16BitBE(file, ei->move_delay_random);
8539
8540   // bits 0 - 15 of "move_pattern" ...
8541   putFile16BitBE(file, ei->move_pattern & 0xffff);
8542   putFile8Bit(file, ei->move_direction_initial);
8543   putFile8Bit(file, ei->move_stepsize);
8544
8545   putFile8Bit(file, ei->slippery_type);
8546
8547   for (y = 0; y < 3; y++)
8548     for (x = 0; x < 3; x++)
8549       putFile16BitBE(file, ei->content.e[x][y]);
8550
8551   putFile16BitBE(file, ei->move_enter_element);
8552   putFile16BitBE(file, ei->move_leave_element);
8553   putFile8Bit(file, ei->move_leave_type);
8554
8555   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8556   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8557
8558   putFile8Bit(file, ei->access_direction);
8559
8560   putFile8Bit(file, ei->explosion_delay);
8561   putFile8Bit(file, ei->ignition_delay);
8562   putFile8Bit(file, ei->explosion_type);
8563
8564   // some free bytes for future custom property values and padding
8565   WriteUnusedBytesToFile(file, 1);
8566
8567   // ---------- change page property values (48 bytes) ------------------------
8568
8569   for (i = 0; i < ei->num_change_pages; i++)
8570   {
8571     struct ElementChangeInfo *change = &ei->change_page[i];
8572     unsigned int event_bits;
8573
8574     // bits 0 - 31 of "has_event[]" ...
8575     event_bits = 0;
8576     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8577       if (change->has_event[j])
8578         event_bits |= (1u << j);
8579     putFile32BitBE(file, event_bits);
8580
8581     putFile16BitBE(file, change->target_element);
8582
8583     putFile16BitBE(file, change->delay_fixed);
8584     putFile16BitBE(file, change->delay_random);
8585     putFile16BitBE(file, change->delay_frames);
8586
8587     putFile16BitBE(file, change->initial_trigger_element);
8588
8589     putFile8Bit(file, change->explode);
8590     putFile8Bit(file, change->use_target_content);
8591     putFile8Bit(file, change->only_if_complete);
8592     putFile8Bit(file, change->use_random_replace);
8593
8594     putFile8Bit(file, change->random_percentage);
8595     putFile8Bit(file, change->replace_when);
8596
8597     for (y = 0; y < 3; y++)
8598       for (x = 0; x < 3; x++)
8599         putFile16BitBE(file, change->target_content.e[x][y]);
8600
8601     putFile8Bit(file, change->can_change);
8602
8603     putFile8Bit(file, change->trigger_side);
8604
8605     putFile8Bit(file, change->trigger_player);
8606     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8607                        log_2(change->trigger_page)));
8608
8609     putFile8Bit(file, change->has_action);
8610     putFile8Bit(file, change->action_type);
8611     putFile8Bit(file, change->action_mode);
8612     putFile16BitBE(file, change->action_arg);
8613
8614     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8615     event_bits = 0;
8616     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8617       if (change->has_event[j])
8618         event_bits |= (1u << (j - 32));
8619     putFile8Bit(file, event_bits);
8620   }
8621 }
8622 #endif
8623
8624 #if ENABLE_HISTORIC_CHUNKS
8625 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8626 {
8627   struct ElementInfo *ei = &element_info[element];
8628   struct ElementGroupInfo *group = ei->group;
8629   int i;
8630
8631   putFile16BitBE(file, element);
8632
8633   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8634     putFile8Bit(file, ei->description[i]);
8635
8636   putFile8Bit(file, group->num_elements);
8637
8638   putFile8Bit(file, ei->use_gfx_element);
8639   putFile16BitBE(file, ei->gfx_element_initial);
8640
8641   putFile8Bit(file, group->choice_mode);
8642
8643   // some free bytes for future values and padding
8644   WriteUnusedBytesToFile(file, 3);
8645
8646   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8647     putFile16BitBE(file, group->element[i]);
8648 }
8649 #endif
8650
8651 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8652                                 boolean write_element)
8653 {
8654   int save_type = entry->save_type;
8655   int data_type = entry->data_type;
8656   int conf_type = entry->conf_type;
8657   int byte_mask = conf_type & CONF_MASK_BYTES;
8658   int element = entry->element;
8659   int default_value = entry->default_value;
8660   int num_bytes = 0;
8661   boolean modified = FALSE;
8662
8663   if (byte_mask != CONF_MASK_MULTI_BYTES)
8664   {
8665     void *value_ptr = entry->value;
8666     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8667                  *(int *)value_ptr);
8668
8669     // check if any settings have been modified before saving them
8670     if (value != default_value)
8671       modified = TRUE;
8672
8673     // do not save if explicitly told or if unmodified default settings
8674     if ((save_type == SAVE_CONF_NEVER) ||
8675         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8676       return 0;
8677
8678     if (write_element)
8679       num_bytes += putFile16BitBE(file, element);
8680
8681     num_bytes += putFile8Bit(file, conf_type);
8682     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8683                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8684                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8685                   0);
8686   }
8687   else if (data_type == TYPE_STRING)
8688   {
8689     char *default_string = entry->default_string;
8690     char *string = (char *)(entry->value);
8691     int string_length = strlen(string);
8692     int i;
8693
8694     // check if any settings have been modified before saving them
8695     if (!strEqual(string, default_string))
8696       modified = TRUE;
8697
8698     // do not save if explicitly told or if unmodified default settings
8699     if ((save_type == SAVE_CONF_NEVER) ||
8700         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8701       return 0;
8702
8703     if (write_element)
8704       num_bytes += putFile16BitBE(file, element);
8705
8706     num_bytes += putFile8Bit(file, conf_type);
8707     num_bytes += putFile16BitBE(file, string_length);
8708
8709     for (i = 0; i < string_length; i++)
8710       num_bytes += putFile8Bit(file, string[i]);
8711   }
8712   else if (data_type == TYPE_ELEMENT_LIST)
8713   {
8714     int *element_array = (int *)(entry->value);
8715     int num_elements = *(int *)(entry->num_entities);
8716     int i;
8717
8718     // check if any settings have been modified before saving them
8719     for (i = 0; i < num_elements; i++)
8720       if (element_array[i] != default_value)
8721         modified = TRUE;
8722
8723     // do not save if explicitly told or if unmodified default settings
8724     if ((save_type == SAVE_CONF_NEVER) ||
8725         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8726       return 0;
8727
8728     if (write_element)
8729       num_bytes += putFile16BitBE(file, element);
8730
8731     num_bytes += putFile8Bit(file, conf_type);
8732     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8733
8734     for (i = 0; i < num_elements; i++)
8735       num_bytes += putFile16BitBE(file, element_array[i]);
8736   }
8737   else if (data_type == TYPE_CONTENT_LIST)
8738   {
8739     struct Content *content = (struct Content *)(entry->value);
8740     int num_contents = *(int *)(entry->num_entities);
8741     int i, x, y;
8742
8743     // check if any settings have been modified before saving them
8744     for (i = 0; i < num_contents; i++)
8745       for (y = 0; y < 3; y++)
8746         for (x = 0; x < 3; x++)
8747           if (content[i].e[x][y] != default_value)
8748             modified = TRUE;
8749
8750     // do not save if explicitly told or if unmodified default settings
8751     if ((save_type == SAVE_CONF_NEVER) ||
8752         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8753       return 0;
8754
8755     if (write_element)
8756       num_bytes += putFile16BitBE(file, element);
8757
8758     num_bytes += putFile8Bit(file, conf_type);
8759     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8760
8761     for (i = 0; i < num_contents; i++)
8762       for (y = 0; y < 3; y++)
8763         for (x = 0; x < 3; x++)
8764           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8765   }
8766
8767   return num_bytes;
8768 }
8769
8770 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8771 {
8772   int chunk_size = 0;
8773   int i;
8774
8775   li = *level;          // copy level data into temporary buffer
8776
8777   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8778     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8779
8780   return chunk_size;
8781 }
8782
8783 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8784 {
8785   int chunk_size = 0;
8786   int i;
8787
8788   li = *level;          // copy level data into temporary buffer
8789
8790   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8791     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8792
8793   return chunk_size;
8794 }
8795
8796 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8797 {
8798   int envelope_nr = element - EL_ENVELOPE_1;
8799   int chunk_size = 0;
8800   int i;
8801
8802   chunk_size += putFile16BitBE(file, element);
8803
8804   // copy envelope data into temporary buffer
8805   xx_envelope = level->envelope[envelope_nr];
8806
8807   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8808     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8809
8810   return chunk_size;
8811 }
8812
8813 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8814 {
8815   struct ElementInfo *ei = &element_info[element];
8816   int chunk_size = 0;
8817   int i, j;
8818
8819   chunk_size += putFile16BitBE(file, element);
8820
8821   xx_ei = *ei;          // copy element data into temporary buffer
8822
8823   // set default description string for this specific element
8824   strcpy(xx_default_description, getDefaultElementDescription(ei));
8825
8826   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8827     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8828
8829   for (i = 0; i < ei->num_change_pages; i++)
8830   {
8831     struct ElementChangeInfo *change = &ei->change_page[i];
8832
8833     xx_current_change_page = i;
8834
8835     xx_change = *change;        // copy change data into temporary buffer
8836
8837     resetEventBits();
8838     setEventBitsFromEventFlags(change);
8839
8840     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8841       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8842                                          FALSE);
8843   }
8844
8845   return chunk_size;
8846 }
8847
8848 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8849 {
8850   struct ElementInfo *ei = &element_info[element];
8851   struct ElementGroupInfo *group = ei->group;
8852   int chunk_size = 0;
8853   int i;
8854
8855   chunk_size += putFile16BitBE(file, element);
8856
8857   xx_ei = *ei;          // copy element data into temporary buffer
8858   xx_group = *group;    // copy group data into temporary buffer
8859
8860   // set default description string for this specific element
8861   strcpy(xx_default_description, getDefaultElementDescription(ei));
8862
8863   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8864     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8865
8866   return chunk_size;
8867 }
8868
8869 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8870 {
8871   struct ElementInfo *ei = &element_info[element];
8872   int chunk_size = 0;
8873   int i;
8874
8875   chunk_size += putFile16BitBE(file, element);
8876
8877   xx_ei = *ei;          // copy element data into temporary buffer
8878
8879   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8880     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8881
8882   return chunk_size;
8883 }
8884
8885 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8886                                   boolean save_as_template)
8887 {
8888   int chunk_size;
8889   int i;
8890   FILE *file;
8891
8892   if (!(file = fopen(filename, MODE_WRITE)))
8893   {
8894     Warn("cannot save level file '%s'", filename);
8895
8896     return;
8897   }
8898
8899   level->file_version = FILE_VERSION_ACTUAL;
8900   level->game_version = GAME_VERSION_ACTUAL;
8901
8902   level->creation_date = getCurrentDate();
8903
8904   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8905   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8906
8907   chunk_size = SaveLevel_VERS(NULL, level);
8908   putFileChunkBE(file, "VERS", chunk_size);
8909   SaveLevel_VERS(file, level);
8910
8911   chunk_size = SaveLevel_DATE(NULL, level);
8912   putFileChunkBE(file, "DATE", chunk_size);
8913   SaveLevel_DATE(file, level);
8914
8915   chunk_size = SaveLevel_NAME(NULL, level);
8916   putFileChunkBE(file, "NAME", chunk_size);
8917   SaveLevel_NAME(file, level);
8918
8919   chunk_size = SaveLevel_AUTH(NULL, level);
8920   putFileChunkBE(file, "AUTH", chunk_size);
8921   SaveLevel_AUTH(file, level);
8922
8923   chunk_size = SaveLevel_INFO(NULL, level);
8924   putFileChunkBE(file, "INFO", chunk_size);
8925   SaveLevel_INFO(file, level);
8926
8927   chunk_size = SaveLevel_BODY(NULL, level);
8928   putFileChunkBE(file, "BODY", chunk_size);
8929   SaveLevel_BODY(file, level);
8930
8931   chunk_size = SaveLevel_ELEM(NULL, level);
8932   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8933   {
8934     putFileChunkBE(file, "ELEM", chunk_size);
8935     SaveLevel_ELEM(file, level);
8936   }
8937
8938   for (i = 0; i < NUM_ENVELOPES; i++)
8939   {
8940     int element = EL_ENVELOPE_1 + i;
8941
8942     chunk_size = SaveLevel_NOTE(NULL, level, element);
8943     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8944     {
8945       putFileChunkBE(file, "NOTE", chunk_size);
8946       SaveLevel_NOTE(file, level, element);
8947     }
8948   }
8949
8950   // if not using template level, check for non-default custom/group elements
8951   if (!level->use_custom_template || save_as_template)
8952   {
8953     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8954     {
8955       int element = EL_CUSTOM_START + i;
8956
8957       chunk_size = SaveLevel_CUSX(NULL, level, element);
8958       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8959       {
8960         putFileChunkBE(file, "CUSX", chunk_size);
8961         SaveLevel_CUSX(file, level, element);
8962       }
8963     }
8964
8965     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8966     {
8967       int element = EL_GROUP_START + i;
8968
8969       chunk_size = SaveLevel_GRPX(NULL, level, element);
8970       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8971       {
8972         putFileChunkBE(file, "GRPX", chunk_size);
8973         SaveLevel_GRPX(file, level, element);
8974       }
8975     }
8976
8977     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8978     {
8979       int element = GET_EMPTY_ELEMENT(i);
8980
8981       chunk_size = SaveLevel_EMPX(NULL, level, element);
8982       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8983       {
8984         putFileChunkBE(file, "EMPX", chunk_size);
8985         SaveLevel_EMPX(file, level, element);
8986       }
8987     }
8988   }
8989
8990   fclose(file);
8991
8992   SetFilePermissions(filename, PERMS_PRIVATE);
8993 }
8994
8995 void SaveLevel(int nr)
8996 {
8997   char *filename = getDefaultLevelFilename(nr);
8998
8999   SaveLevelFromFilename(&level, filename, FALSE);
9000 }
9001
9002 void SaveLevelTemplate(void)
9003 {
9004   char *filename = getLocalLevelTemplateFilename();
9005
9006   SaveLevelFromFilename(&level, filename, TRUE);
9007 }
9008
9009 boolean SaveLevelChecked(int nr)
9010 {
9011   char *filename = getDefaultLevelFilename(nr);
9012   boolean new_level = !fileExists(filename);
9013   boolean level_saved = FALSE;
9014
9015   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
9016   {
9017     SaveLevel(nr);
9018
9019     if (new_level)
9020       Request("Level saved!", REQ_CONFIRM);
9021
9022     level_saved = TRUE;
9023   }
9024
9025   return level_saved;
9026 }
9027
9028 void DumpLevel(struct LevelInfo *level)
9029 {
9030   if (level->no_level_file || level->no_valid_file)
9031   {
9032     Warn("cannot dump -- no valid level file found");
9033
9034     return;
9035   }
9036
9037   PrintLine("-", 79);
9038   Print("Level xxx (file version %08d, game version %08d)\n",
9039         level->file_version, level->game_version);
9040   PrintLine("-", 79);
9041
9042   Print("Level author: '%s'\n", level->author);
9043   Print("Level title:  '%s'\n", level->name);
9044   Print("\n");
9045   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
9046   Print("\n");
9047   Print("Level time:  %d seconds\n", level->time);
9048   Print("Gems needed: %d\n", level->gems_needed);
9049   Print("\n");
9050   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
9051   Print("Time for wheel:      %d seconds\n", level->time_wheel);
9052   Print("Time for light:      %d seconds\n", level->time_light);
9053   Print("Time for timegate:   %d seconds\n", level->time_timegate);
9054   Print("\n");
9055   Print("Amoeba speed: %d\n", level->amoeba_speed);
9056   Print("\n");
9057
9058   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
9059   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
9060   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
9061   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
9062   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
9063   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9064
9065   if (options.debug)
9066   {
9067     int i, j;
9068
9069     for (i = 0; i < NUM_ENVELOPES; i++)
9070     {
9071       char *text = level->envelope[i].text;
9072       int text_len = strlen(text);
9073       boolean has_text = FALSE;
9074
9075       for (j = 0; j < text_len; j++)
9076         if (text[j] != ' ' && text[j] != '\n')
9077           has_text = TRUE;
9078
9079       if (has_text)
9080       {
9081         Print("\n");
9082         Print("Envelope %d:\n'%s'\n", i + 1, text);
9083       }
9084     }
9085   }
9086
9087   PrintLine("-", 79);
9088 }
9089
9090 void DumpLevels(void)
9091 {
9092   static LevelDirTree *dumplevel_leveldir = NULL;
9093
9094   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9095                                                  global.dumplevel_leveldir);
9096
9097   if (dumplevel_leveldir == NULL)
9098     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9099
9100   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9101       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9102     Fail("no such level number: %d", global.dumplevel_level_nr);
9103
9104   leveldir_current = dumplevel_leveldir;
9105
9106   LoadLevel(global.dumplevel_level_nr);
9107   DumpLevel(&level);
9108
9109   CloseAllAndExit(0);
9110 }
9111
9112 void DumpLevelsetFromFilename_BD(char *filename)
9113 {
9114   if (leveldir_current == NULL) // no levelsets loaded yet
9115     bd_open_all();
9116
9117   if (!LoadNativeLevel_BD(filename, 0, FALSE))
9118     CloseAllAndExit(0);         // function has already printed warning
9119
9120   PrintLine("-", 79);
9121   Print("Levelset '%s'\n", filename);
9122   PrintLine("-", 79);
9123
9124   DumpLevelset_BD();
9125
9126   PrintLine("-", 79);
9127
9128   CloseAllAndExit(0);
9129 }
9130
9131 void DumpLevelset(void)
9132 {
9133   static LevelDirTree *dumplevelset_leveldir = NULL;
9134
9135   dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9136                                                     global.dumplevelset_leveldir);
9137   if (dumplevelset_leveldir == NULL)
9138     Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9139
9140   PrintLine("-", 79);
9141   Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9142   PrintLine("-", 79);
9143
9144   Print("Number of levels:   %d\n", dumplevelset_leveldir->levels);
9145   Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9146
9147   PrintLine("-", 79);
9148
9149   CloseAllAndExit(0);
9150 }
9151
9152
9153 // ============================================================================
9154 // tape file functions
9155 // ============================================================================
9156
9157 static void setTapeInfoToDefaults(void)
9158 {
9159   int i;
9160
9161   // always start with reliable default values (empty tape)
9162   TapeErase();
9163
9164   // default values (also for pre-1.2 tapes) with only the first player
9165   tape.player_participates[0] = TRUE;
9166   for (i = 1; i < MAX_PLAYERS; i++)
9167     tape.player_participates[i] = FALSE;
9168
9169   // at least one (default: the first) player participates in every tape
9170   tape.num_participating_players = 1;
9171
9172   tape.property_bits = TAPE_PROPERTY_NONE;
9173
9174   tape.level_nr = level_nr;
9175   tape.counter = 0;
9176   tape.changed = FALSE;
9177   tape.solved = FALSE;
9178
9179   tape.recording = FALSE;
9180   tape.playing = FALSE;
9181   tape.pausing = FALSE;
9182
9183   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9184   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9185
9186   tape.no_info_chunk = TRUE;
9187   tape.no_valid_file = FALSE;
9188 }
9189
9190 static int getTapePosSize(struct TapeInfo *tape)
9191 {
9192   int tape_pos_size = 0;
9193
9194   if (tape->use_key_actions)
9195     tape_pos_size += tape->num_participating_players;
9196
9197   if (tape->use_mouse_actions)
9198     tape_pos_size += 3;         // x and y position and mouse button mask
9199
9200   tape_pos_size += 1;           // tape action delay value
9201
9202   return tape_pos_size;
9203 }
9204
9205 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9206 {
9207   tape->use_key_actions = FALSE;
9208   tape->use_mouse_actions = FALSE;
9209
9210   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9211     tape->use_key_actions = TRUE;
9212
9213   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9214     tape->use_mouse_actions = TRUE;
9215 }
9216
9217 static int getTapeActionValue(struct TapeInfo *tape)
9218 {
9219   return (tape->use_key_actions &&
9220           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9221           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9222           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9223           TAPE_ACTIONS_DEFAULT);
9224 }
9225
9226 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9227 {
9228   tape->file_version = getFileVersion(file);
9229   tape->game_version = getFileVersion(file);
9230
9231   return chunk_size;
9232 }
9233
9234 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9235 {
9236   int i;
9237
9238   tape->random_seed = getFile32BitBE(file);
9239   tape->date        = getFile32BitBE(file);
9240   tape->length      = getFile32BitBE(file);
9241
9242   // read header fields that are new since version 1.2
9243   if (tape->file_version >= FILE_VERSION_1_2)
9244   {
9245     byte store_participating_players = getFile8Bit(file);
9246     int engine_version;
9247
9248     // since version 1.2, tapes store which players participate in the tape
9249     tape->num_participating_players = 0;
9250     for (i = 0; i < MAX_PLAYERS; i++)
9251     {
9252       tape->player_participates[i] = FALSE;
9253
9254       if (store_participating_players & (1 << i))
9255       {
9256         tape->player_participates[i] = TRUE;
9257         tape->num_participating_players++;
9258       }
9259     }
9260
9261     setTapeActionFlags(tape, getFile8Bit(file));
9262
9263     tape->property_bits = getFile8Bit(file);
9264     tape->solved = getFile8Bit(file);
9265
9266     engine_version = getFileVersion(file);
9267     if (engine_version > 0)
9268       tape->engine_version = engine_version;
9269     else
9270       tape->engine_version = tape->game_version;
9271   }
9272
9273   return chunk_size;
9274 }
9275
9276 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9277 {
9278   tape->scr_fieldx = getFile8Bit(file);
9279   tape->scr_fieldy = getFile8Bit(file);
9280
9281   return chunk_size;
9282 }
9283
9284 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9285 {
9286   char *level_identifier = NULL;
9287   int level_identifier_size;
9288   int i;
9289
9290   tape->no_info_chunk = FALSE;
9291
9292   level_identifier_size = getFile16BitBE(file);
9293
9294   level_identifier = checked_malloc(level_identifier_size);
9295
9296   for (i = 0; i < level_identifier_size; i++)
9297     level_identifier[i] = getFile8Bit(file);
9298
9299   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9300   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9301
9302   checked_free(level_identifier);
9303
9304   tape->level_nr = getFile16BitBE(file);
9305
9306   chunk_size = 2 + level_identifier_size + 2;
9307
9308   return chunk_size;
9309 }
9310
9311 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9312 {
9313   int i, j;
9314   int tape_pos_size = getTapePosSize(tape);
9315   int chunk_size_expected = tape_pos_size * tape->length;
9316
9317   if (chunk_size_expected != chunk_size)
9318   {
9319     ReadUnusedBytesFromFile(file, chunk_size);
9320     return chunk_size_expected;
9321   }
9322
9323   for (i = 0; i < tape->length; i++)
9324   {
9325     if (i >= MAX_TAPE_LEN)
9326     {
9327       Warn("tape truncated -- size exceeds maximum tape size %d",
9328             MAX_TAPE_LEN);
9329
9330       // tape too large; read and ignore remaining tape data from this chunk
9331       for (;i < tape->length; i++)
9332         ReadUnusedBytesFromFile(file, tape_pos_size);
9333
9334       break;
9335     }
9336
9337     if (tape->use_key_actions)
9338     {
9339       for (j = 0; j < MAX_PLAYERS; j++)
9340       {
9341         tape->pos[i].action[j] = MV_NONE;
9342
9343         if (tape->player_participates[j])
9344           tape->pos[i].action[j] = getFile8Bit(file);
9345       }
9346     }
9347
9348     if (tape->use_mouse_actions)
9349     {
9350       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9351       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9352       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9353     }
9354
9355     tape->pos[i].delay = getFile8Bit(file);
9356
9357     if (tape->file_version == FILE_VERSION_1_0)
9358     {
9359       // eliminate possible diagonal moves in old tapes
9360       // this is only for backward compatibility
9361
9362       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9363       byte action = tape->pos[i].action[0];
9364       int k, num_moves = 0;
9365
9366       for (k = 0; k < 4; k++)
9367       {
9368         if (action & joy_dir[k])
9369         {
9370           tape->pos[i + num_moves].action[0] = joy_dir[k];
9371           if (num_moves > 0)
9372             tape->pos[i + num_moves].delay = 0;
9373           num_moves++;
9374         }
9375       }
9376
9377       if (num_moves > 1)
9378       {
9379         num_moves--;
9380         i += num_moves;
9381         tape->length += num_moves;
9382       }
9383     }
9384     else if (tape->file_version < FILE_VERSION_2_0)
9385     {
9386       // convert pre-2.0 tapes to new tape format
9387
9388       if (tape->pos[i].delay > 1)
9389       {
9390         // action part
9391         tape->pos[i + 1] = tape->pos[i];
9392         tape->pos[i + 1].delay = 1;
9393
9394         // delay part
9395         for (j = 0; j < MAX_PLAYERS; j++)
9396           tape->pos[i].action[j] = MV_NONE;
9397         tape->pos[i].delay--;
9398
9399         i++;
9400         tape->length++;
9401       }
9402     }
9403
9404     if (checkEndOfFile(file))
9405       break;
9406   }
9407
9408   if (i != tape->length)
9409     chunk_size = tape_pos_size * i;
9410
9411   return chunk_size;
9412 }
9413
9414 static void LoadTape_SokobanSolution(char *filename)
9415 {
9416   File *file;
9417   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9418
9419   if (!(file = openFile(filename, MODE_READ)))
9420   {
9421     tape.no_valid_file = TRUE;
9422
9423     return;
9424   }
9425
9426   while (!checkEndOfFile(file))
9427   {
9428     unsigned char c = getByteFromFile(file);
9429
9430     if (checkEndOfFile(file))
9431       break;
9432
9433     switch (c)
9434     {
9435       case 'u':
9436       case 'U':
9437         tape.pos[tape.length].action[0] = MV_UP;
9438         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9439         tape.length++;
9440         break;
9441
9442       case 'd':
9443       case 'D':
9444         tape.pos[tape.length].action[0] = MV_DOWN;
9445         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9446         tape.length++;
9447         break;
9448
9449       case 'l':
9450       case 'L':
9451         tape.pos[tape.length].action[0] = MV_LEFT;
9452         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9453         tape.length++;
9454         break;
9455
9456       case 'r':
9457       case 'R':
9458         tape.pos[tape.length].action[0] = MV_RIGHT;
9459         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9460         tape.length++;
9461         break;
9462
9463       case '\n':
9464       case '\r':
9465       case '\t':
9466       case ' ':
9467         // ignore white-space characters
9468         break;
9469
9470       default:
9471         tape.no_valid_file = TRUE;
9472
9473         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9474
9475         break;
9476     }
9477   }
9478
9479   closeFile(file);
9480
9481   if (tape.no_valid_file)
9482     return;
9483
9484   tape.length_frames  = GetTapeLengthFrames();
9485   tape.length_seconds = GetTapeLengthSeconds();
9486 }
9487
9488 void LoadTapeFromFilename(char *filename)
9489 {
9490   char cookie[MAX_LINE_LEN];
9491   char chunk_name[CHUNK_ID_LEN + 1];
9492   File *file;
9493   int chunk_size;
9494
9495   // always start with reliable default values
9496   setTapeInfoToDefaults();
9497
9498   if (strSuffix(filename, ".sln"))
9499   {
9500     LoadTape_SokobanSolution(filename);
9501
9502     return;
9503   }
9504
9505   if (!(file = openFile(filename, MODE_READ)))
9506   {
9507     tape.no_valid_file = TRUE;
9508
9509     return;
9510   }
9511
9512   getFileChunkBE(file, chunk_name, NULL);
9513   if (strEqual(chunk_name, "RND1"))
9514   {
9515     getFile32BitBE(file);               // not used
9516
9517     getFileChunkBE(file, chunk_name, NULL);
9518     if (!strEqual(chunk_name, "TAPE"))
9519     {
9520       tape.no_valid_file = TRUE;
9521
9522       Warn("unknown format of tape file '%s'", filename);
9523
9524       closeFile(file);
9525
9526       return;
9527     }
9528   }
9529   else  // check for pre-2.0 file format with cookie string
9530   {
9531     strcpy(cookie, chunk_name);
9532     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9533       cookie[4] = '\0';
9534     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9535       cookie[strlen(cookie) - 1] = '\0';
9536
9537     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9538     {
9539       tape.no_valid_file = TRUE;
9540
9541       Warn("unknown format of tape file '%s'", filename);
9542
9543       closeFile(file);
9544
9545       return;
9546     }
9547
9548     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9549     {
9550       tape.no_valid_file = TRUE;
9551
9552       Warn("unsupported version of tape file '%s'", filename);
9553
9554       closeFile(file);
9555
9556       return;
9557     }
9558
9559     // pre-2.0 tape files have no game version, so use file version here
9560     tape.game_version = tape.file_version;
9561   }
9562
9563   if (tape.file_version < FILE_VERSION_1_2)
9564   {
9565     // tape files from versions before 1.2.0 without chunk structure
9566     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9567     LoadTape_BODY(file, 2 * tape.length,      &tape);
9568   }
9569   else
9570   {
9571     static struct
9572     {
9573       char *name;
9574       int size;
9575       int (*loader)(File *, int, struct TapeInfo *);
9576     }
9577     chunk_info[] =
9578     {
9579       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9580       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9581       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9582       { "INFO", -1,                     LoadTape_INFO },
9583       { "BODY", -1,                     LoadTape_BODY },
9584       {  NULL,  0,                      NULL }
9585     };
9586
9587     while (getFileChunkBE(file, chunk_name, &chunk_size))
9588     {
9589       int i = 0;
9590
9591       while (chunk_info[i].name != NULL &&
9592              !strEqual(chunk_name, chunk_info[i].name))
9593         i++;
9594
9595       if (chunk_info[i].name == NULL)
9596       {
9597         Warn("unknown chunk '%s' in tape file '%s'",
9598               chunk_name, filename);
9599
9600         ReadUnusedBytesFromFile(file, chunk_size);
9601       }
9602       else if (chunk_info[i].size != -1 &&
9603                chunk_info[i].size != chunk_size)
9604       {
9605         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9606               chunk_size, chunk_name, filename);
9607
9608         ReadUnusedBytesFromFile(file, chunk_size);
9609       }
9610       else
9611       {
9612         // call function to load this tape chunk
9613         int chunk_size_expected =
9614           (chunk_info[i].loader)(file, chunk_size, &tape);
9615
9616         // the size of some chunks cannot be checked before reading other
9617         // chunks first (like "HEAD" and "BODY") that contain some header
9618         // information, so check them here
9619         if (chunk_size_expected != chunk_size)
9620         {
9621           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9622                 chunk_size, chunk_name, filename);
9623         }
9624       }
9625     }
9626   }
9627
9628   closeFile(file);
9629
9630   tape.length_frames  = GetTapeLengthFrames();
9631   tape.length_seconds = GetTapeLengthSeconds();
9632
9633 #if 0
9634   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9635         tape.file_version);
9636   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9637         tape.game_version);
9638   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9639         tape.engine_version);
9640 #endif
9641 }
9642
9643 void LoadTape(int nr)
9644 {
9645   char *filename = getTapeFilename(nr);
9646
9647   LoadTapeFromFilename(filename);
9648 }
9649
9650 void LoadSolutionTape(int nr)
9651 {
9652   char *filename = getSolutionTapeFilename(nr);
9653
9654   LoadTapeFromFilename(filename);
9655
9656   if (TAPE_IS_EMPTY(tape))
9657   {
9658     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9659         level.native_bd_level->replay != NULL)
9660       CopyNativeTape_BD_to_RND(&level);
9661     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9662         level.native_sp_level->demo.is_available)
9663       CopyNativeTape_SP_to_RND(&level);
9664   }
9665 }
9666
9667 void LoadScoreTape(char *score_tape_basename, int nr)
9668 {
9669   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9670
9671   LoadTapeFromFilename(filename);
9672 }
9673
9674 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9675 {
9676   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9677
9678   LoadTapeFromFilename(filename);
9679 }
9680
9681 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9682 {
9683   // chunk required for team mode tapes with non-default screen size
9684   return (tape->num_participating_players > 1 &&
9685           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9686            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9687 }
9688
9689 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9690 {
9691   putFileVersion(file, tape->file_version);
9692   putFileVersion(file, tape->game_version);
9693 }
9694
9695 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9696 {
9697   int i;
9698   byte store_participating_players = 0;
9699
9700   // set bits for participating players for compact storage
9701   for (i = 0; i < MAX_PLAYERS; i++)
9702     if (tape->player_participates[i])
9703       store_participating_players |= (1 << i);
9704
9705   putFile32BitBE(file, tape->random_seed);
9706   putFile32BitBE(file, tape->date);
9707   putFile32BitBE(file, tape->length);
9708
9709   putFile8Bit(file, store_participating_players);
9710
9711   putFile8Bit(file, getTapeActionValue(tape));
9712
9713   putFile8Bit(file, tape->property_bits);
9714   putFile8Bit(file, tape->solved);
9715
9716   putFileVersion(file, tape->engine_version);
9717 }
9718
9719 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9720 {
9721   putFile8Bit(file, tape->scr_fieldx);
9722   putFile8Bit(file, tape->scr_fieldy);
9723 }
9724
9725 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9726 {
9727   int level_identifier_size = strlen(tape->level_identifier) + 1;
9728   int i;
9729
9730   putFile16BitBE(file, level_identifier_size);
9731
9732   for (i = 0; i < level_identifier_size; i++)
9733     putFile8Bit(file, tape->level_identifier[i]);
9734
9735   putFile16BitBE(file, tape->level_nr);
9736 }
9737
9738 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9739 {
9740   int i, j;
9741
9742   for (i = 0; i < tape->length; i++)
9743   {
9744     if (tape->use_key_actions)
9745     {
9746       for (j = 0; j < MAX_PLAYERS; j++)
9747         if (tape->player_participates[j])
9748           putFile8Bit(file, tape->pos[i].action[j]);
9749     }
9750
9751     if (tape->use_mouse_actions)
9752     {
9753       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9754       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9755       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9756     }
9757
9758     putFile8Bit(file, tape->pos[i].delay);
9759   }
9760 }
9761
9762 void SaveTapeToFilename(char *filename)
9763 {
9764   FILE *file;
9765   int tape_pos_size;
9766   int info_chunk_size;
9767   int body_chunk_size;
9768
9769   if (!(file = fopen(filename, MODE_WRITE)))
9770   {
9771     Warn("cannot save level recording file '%s'", filename);
9772
9773     return;
9774   }
9775
9776   tape_pos_size = getTapePosSize(&tape);
9777
9778   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9779   body_chunk_size = tape_pos_size * tape.length;
9780
9781   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9782   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9783
9784   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9785   SaveTape_VERS(file, &tape);
9786
9787   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9788   SaveTape_HEAD(file, &tape);
9789
9790   if (checkSaveTape_SCRN(&tape))
9791   {
9792     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9793     SaveTape_SCRN(file, &tape);
9794   }
9795
9796   putFileChunkBE(file, "INFO", info_chunk_size);
9797   SaveTape_INFO(file, &tape);
9798
9799   putFileChunkBE(file, "BODY", body_chunk_size);
9800   SaveTape_BODY(file, &tape);
9801
9802   fclose(file);
9803
9804   SetFilePermissions(filename, PERMS_PRIVATE);
9805 }
9806
9807 static void SaveTapeExt(char *filename)
9808 {
9809   int i;
9810
9811   tape.file_version = FILE_VERSION_ACTUAL;
9812   tape.game_version = GAME_VERSION_ACTUAL;
9813
9814   tape.num_participating_players = 0;
9815
9816   // count number of participating players
9817   for (i = 0; i < MAX_PLAYERS; i++)
9818     if (tape.player_participates[i])
9819       tape.num_participating_players++;
9820
9821   SaveTapeToFilename(filename);
9822
9823   tape.changed = FALSE;
9824 }
9825
9826 void SaveTape(int nr)
9827 {
9828   char *filename = getTapeFilename(nr);
9829
9830   InitTapeDirectory(leveldir_current->subdir);
9831
9832   SaveTapeExt(filename);
9833 }
9834
9835 void SaveScoreTape(int nr)
9836 {
9837   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9838
9839   // used instead of "leveldir_current->subdir" (for network games)
9840   InitScoreTapeDirectory(levelset.identifier, nr);
9841
9842   SaveTapeExt(filename);
9843 }
9844
9845 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9846                                   unsigned int req_state_added)
9847 {
9848   char *filename = getTapeFilename(nr);
9849   boolean new_tape = !fileExists(filename);
9850   boolean tape_saved = FALSE;
9851
9852   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9853   {
9854     SaveTape(nr);
9855
9856     if (new_tape)
9857       Request(msg_saved, REQ_CONFIRM | req_state_added);
9858
9859     tape_saved = TRUE;
9860   }
9861
9862   return tape_saved;
9863 }
9864
9865 boolean SaveTapeChecked(int nr)
9866 {
9867   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9868 }
9869
9870 boolean SaveTapeChecked_LevelSolved(int nr)
9871 {
9872   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9873                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9874 }
9875
9876 void DumpTape(struct TapeInfo *tape)
9877 {
9878   int tape_frame_counter;
9879   int i, j;
9880
9881   if (tape->no_valid_file)
9882   {
9883     Warn("cannot dump -- no valid tape file found");
9884
9885     return;
9886   }
9887
9888   PrintLine("-", 79);
9889
9890   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9891         tape->level_nr, tape->file_version, tape->game_version);
9892   Print("                  (effective engine version %08d)\n",
9893         tape->engine_version);
9894   Print("Level series identifier: '%s'\n", tape->level_identifier);
9895
9896   Print("Solution tape: %s\n",
9897         tape->solved ? "yes" :
9898         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9899
9900   Print("Special tape properties: ");
9901   if (tape->property_bits == TAPE_PROPERTY_NONE)
9902     Print("[none]");
9903   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9904     Print("[em_random_bug]");
9905   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9906     Print("[game_speed]");
9907   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9908     Print("[pause]");
9909   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9910     Print("[single_step]");
9911   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9912     Print("[snapshot]");
9913   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9914     Print("[replayed]");
9915   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9916     Print("[tas_keys]");
9917   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9918     Print("[small_graphics]");
9919   Print("\n");
9920
9921   int year2 = tape->date / 10000;
9922   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9923   int month_index_raw = (tape->date / 100) % 100;
9924   int month_index = month_index_raw % 12;       // prevent invalid index
9925   int month = month_index + 1;
9926   int day = tape->date % 100;
9927
9928   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9929
9930   PrintLine("-", 79);
9931
9932   tape_frame_counter = 0;
9933
9934   for (i = 0; i < tape->length; i++)
9935   {
9936     if (i >= MAX_TAPE_LEN)
9937       break;
9938
9939     Print("%04d: ", i);
9940
9941     for (j = 0; j < MAX_PLAYERS; j++)
9942     {
9943       if (tape->player_participates[j])
9944       {
9945         int action = tape->pos[i].action[j];
9946
9947         Print("%d:%02x ", j, action);
9948         Print("[%c%c%c%c|%c%c] - ",
9949               (action & JOY_LEFT ? '<' : ' '),
9950               (action & JOY_RIGHT ? '>' : ' '),
9951               (action & JOY_UP ? '^' : ' '),
9952               (action & JOY_DOWN ? 'v' : ' '),
9953               (action & JOY_BUTTON_1 ? '1' : ' '),
9954               (action & JOY_BUTTON_2 ? '2' : ' '));
9955       }
9956     }
9957
9958     Print("(%03d) ", tape->pos[i].delay);
9959     Print("[%05d]\n", tape_frame_counter);
9960
9961     tape_frame_counter += tape->pos[i].delay;
9962   }
9963
9964   PrintLine("-", 79);
9965 }
9966
9967 void DumpTapes(void)
9968 {
9969   static LevelDirTree *dumptape_leveldir = NULL;
9970
9971   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9972                                                 global.dumptape_leveldir);
9973
9974   if (dumptape_leveldir == NULL)
9975     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9976
9977   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9978       global.dumptape_level_nr > dumptape_leveldir->last_level)
9979     Fail("no such level number: %d", global.dumptape_level_nr);
9980
9981   leveldir_current = dumptape_leveldir;
9982
9983   if (options.mytapes)
9984     LoadTape(global.dumptape_level_nr);
9985   else
9986     LoadSolutionTape(global.dumptape_level_nr);
9987
9988   DumpTape(&tape);
9989
9990   CloseAllAndExit(0);
9991 }
9992
9993
9994 // ============================================================================
9995 // score file functions
9996 // ============================================================================
9997
9998 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9999 {
10000   int i;
10001
10002   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10003   {
10004     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
10005     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
10006     scores->entry[i].score = 0;
10007     scores->entry[i].time = 0;
10008
10009     scores->entry[i].id = -1;
10010     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
10011     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
10012     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
10013     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
10014     strcpy(scores->entry[i].country_code, "??");
10015   }
10016
10017   scores->num_entries = 0;
10018   scores->last_added = -1;
10019   scores->last_added_local = -1;
10020
10021   scores->updated = FALSE;
10022   scores->uploaded = FALSE;
10023   scores->tape_downloaded = FALSE;
10024   scores->force_last_added = FALSE;
10025
10026   // The following values are intentionally not reset here:
10027   // - last_level_nr
10028   // - last_entry_nr
10029   // - next_level_nr
10030   // - continue_playing
10031   // - continue_on_return
10032 }
10033
10034 static void setScoreInfoToDefaults(void)
10035 {
10036   setScoreInfoToDefaultsExt(&scores);
10037 }
10038
10039 static void setServerScoreInfoToDefaults(void)
10040 {
10041   setScoreInfoToDefaultsExt(&server_scores);
10042 }
10043
10044 static void LoadScore_OLD(int nr)
10045 {
10046   int i;
10047   char *filename = getScoreFilename(nr);
10048   char cookie[MAX_LINE_LEN];
10049   char line[MAX_LINE_LEN];
10050   char *line_ptr;
10051   FILE *file;
10052
10053   if (!(file = fopen(filename, MODE_READ)))
10054     return;
10055
10056   // check file identifier
10057   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
10058     cookie[0] = '\0';
10059   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10060     cookie[strlen(cookie) - 1] = '\0';
10061
10062   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10063   {
10064     Warn("unknown format of score file '%s'", filename);
10065
10066     fclose(file);
10067
10068     return;
10069   }
10070
10071   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10072   {
10073     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10074       Warn("fscanf() failed; %s", strerror(errno));
10075
10076     if (fgets(line, MAX_LINE_LEN, file) == NULL)
10077       line[0] = '\0';
10078
10079     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10080       line[strlen(line) - 1] = '\0';
10081
10082     for (line_ptr = line; *line_ptr; line_ptr++)
10083     {
10084       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10085       {
10086         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10087         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10088         break;
10089       }
10090     }
10091   }
10092
10093   fclose(file);
10094 }
10095
10096 static void ConvertScore_OLD(void)
10097 {
10098   // only convert score to time for levels that rate playing time over score
10099   if (!level.rate_time_over_score)
10100     return;
10101
10102   // convert old score to playing time for score-less levels (like Supaplex)
10103   int time_final_max = 999;
10104   int i;
10105
10106   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10107   {
10108     int score = scores.entry[i].score;
10109
10110     if (score > 0 && score < time_final_max)
10111       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10112   }
10113 }
10114
10115 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10116 {
10117   scores->file_version = getFileVersion(file);
10118   scores->game_version = getFileVersion(file);
10119
10120   return chunk_size;
10121 }
10122
10123 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10124 {
10125   char *level_identifier = NULL;
10126   int level_identifier_size;
10127   int i;
10128
10129   level_identifier_size = getFile16BitBE(file);
10130
10131   level_identifier = checked_malloc(level_identifier_size);
10132
10133   for (i = 0; i < level_identifier_size; i++)
10134     level_identifier[i] = getFile8Bit(file);
10135
10136   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10137   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10138
10139   checked_free(level_identifier);
10140
10141   scores->level_nr = getFile16BitBE(file);
10142   scores->num_entries = getFile16BitBE(file);
10143
10144   chunk_size = 2 + level_identifier_size + 2 + 2;
10145
10146   return chunk_size;
10147 }
10148
10149 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10150 {
10151   int i, j;
10152
10153   for (i = 0; i < scores->num_entries; i++)
10154   {
10155     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10156       scores->entry[i].name[j] = getFile8Bit(file);
10157
10158     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10159   }
10160
10161   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10162
10163   return chunk_size;
10164 }
10165
10166 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10167 {
10168   int i;
10169
10170   for (i = 0; i < scores->num_entries; i++)
10171     scores->entry[i].score = getFile16BitBE(file);
10172
10173   chunk_size = scores->num_entries * 2;
10174
10175   return chunk_size;
10176 }
10177
10178 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10179 {
10180   int i;
10181
10182   for (i = 0; i < scores->num_entries; i++)
10183     scores->entry[i].score = getFile32BitBE(file);
10184
10185   chunk_size = scores->num_entries * 4;
10186
10187   return chunk_size;
10188 }
10189
10190 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10191 {
10192   int i;
10193
10194   for (i = 0; i < scores->num_entries; i++)
10195     scores->entry[i].time = getFile32BitBE(file);
10196
10197   chunk_size = scores->num_entries * 4;
10198
10199   return chunk_size;
10200 }
10201
10202 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10203 {
10204   int i, j;
10205
10206   for (i = 0; i < scores->num_entries; i++)
10207   {
10208     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10209       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10210
10211     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10212   }
10213
10214   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10215
10216   return chunk_size;
10217 }
10218
10219 void LoadScore(int nr)
10220 {
10221   char *filename = getScoreFilename(nr);
10222   char cookie[MAX_LINE_LEN];
10223   char chunk_name[CHUNK_ID_LEN + 1];
10224   int chunk_size;
10225   boolean old_score_file_format = FALSE;
10226   File *file;
10227
10228   // always start with reliable default values
10229   setScoreInfoToDefaults();
10230
10231   if (!(file = openFile(filename, MODE_READ)))
10232     return;
10233
10234   getFileChunkBE(file, chunk_name, NULL);
10235   if (strEqual(chunk_name, "RND1"))
10236   {
10237     getFile32BitBE(file);               // not used
10238
10239     getFileChunkBE(file, chunk_name, NULL);
10240     if (!strEqual(chunk_name, "SCOR"))
10241     {
10242       Warn("unknown format of score file '%s'", filename);
10243
10244       closeFile(file);
10245
10246       return;
10247     }
10248   }
10249   else  // check for old file format with cookie string
10250   {
10251     strcpy(cookie, chunk_name);
10252     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10253       cookie[4] = '\0';
10254     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10255       cookie[strlen(cookie) - 1] = '\0';
10256
10257     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10258     {
10259       Warn("unknown format of score file '%s'", filename);
10260
10261       closeFile(file);
10262
10263       return;
10264     }
10265
10266     old_score_file_format = TRUE;
10267   }
10268
10269   if (old_score_file_format)
10270   {
10271     // score files from versions before 4.2.4.0 without chunk structure
10272     LoadScore_OLD(nr);
10273
10274     // convert score to time, if possible (mainly for Supaplex levels)
10275     ConvertScore_OLD();
10276   }
10277   else
10278   {
10279     static struct
10280     {
10281       char *name;
10282       int size;
10283       int (*loader)(File *, int, struct ScoreInfo *);
10284     }
10285     chunk_info[] =
10286     {
10287       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10288       { "INFO", -1,                     LoadScore_INFO },
10289       { "NAME", -1,                     LoadScore_NAME },
10290       { "SCOR", -1,                     LoadScore_SCOR },
10291       { "SC4R", -1,                     LoadScore_SC4R },
10292       { "TIME", -1,                     LoadScore_TIME },
10293       { "TAPE", -1,                     LoadScore_TAPE },
10294
10295       {  NULL,  0,                      NULL }
10296     };
10297
10298     while (getFileChunkBE(file, chunk_name, &chunk_size))
10299     {
10300       int i = 0;
10301
10302       while (chunk_info[i].name != NULL &&
10303              !strEqual(chunk_name, chunk_info[i].name))
10304         i++;
10305
10306       if (chunk_info[i].name == NULL)
10307       {
10308         Warn("unknown chunk '%s' in score file '%s'",
10309               chunk_name, filename);
10310
10311         ReadUnusedBytesFromFile(file, chunk_size);
10312       }
10313       else if (chunk_info[i].size != -1 &&
10314                chunk_info[i].size != chunk_size)
10315       {
10316         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10317               chunk_size, chunk_name, filename);
10318
10319         ReadUnusedBytesFromFile(file, chunk_size);
10320       }
10321       else
10322       {
10323         // call function to load this score chunk
10324         int chunk_size_expected =
10325           (chunk_info[i].loader)(file, chunk_size, &scores);
10326
10327         // the size of some chunks cannot be checked before reading other
10328         // chunks first (like "HEAD" and "BODY") that contain some header
10329         // information, so check them here
10330         if (chunk_size_expected != chunk_size)
10331         {
10332           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10333                 chunk_size, chunk_name, filename);
10334         }
10335       }
10336     }
10337   }
10338
10339   closeFile(file);
10340 }
10341
10342 #if ENABLE_HISTORIC_CHUNKS
10343 void SaveScore_OLD(int nr)
10344 {
10345   int i;
10346   char *filename = getScoreFilename(nr);
10347   FILE *file;
10348
10349   // used instead of "leveldir_current->subdir" (for network games)
10350   InitScoreDirectory(levelset.identifier);
10351
10352   if (!(file = fopen(filename, MODE_WRITE)))
10353   {
10354     Warn("cannot save score for level %d", nr);
10355
10356     return;
10357   }
10358
10359   fprintf(file, "%s\n\n", SCORE_COOKIE);
10360
10361   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10362     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10363
10364   fclose(file);
10365
10366   SetFilePermissions(filename, PERMS_PRIVATE);
10367 }
10368 #endif
10369
10370 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10371 {
10372   putFileVersion(file, scores->file_version);
10373   putFileVersion(file, scores->game_version);
10374 }
10375
10376 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10377 {
10378   int level_identifier_size = strlen(scores->level_identifier) + 1;
10379   int i;
10380
10381   putFile16BitBE(file, level_identifier_size);
10382
10383   for (i = 0; i < level_identifier_size; i++)
10384     putFile8Bit(file, scores->level_identifier[i]);
10385
10386   putFile16BitBE(file, scores->level_nr);
10387   putFile16BitBE(file, scores->num_entries);
10388 }
10389
10390 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10391 {
10392   int i, j;
10393
10394   for (i = 0; i < scores->num_entries; i++)
10395   {
10396     int name_size = strlen(scores->entry[i].name);
10397
10398     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10399       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10400   }
10401 }
10402
10403 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10404 {
10405   int i;
10406
10407   for (i = 0; i < scores->num_entries; i++)
10408     putFile16BitBE(file, scores->entry[i].score);
10409 }
10410
10411 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10412 {
10413   int i;
10414
10415   for (i = 0; i < scores->num_entries; i++)
10416     putFile32BitBE(file, scores->entry[i].score);
10417 }
10418
10419 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10420 {
10421   int i;
10422
10423   for (i = 0; i < scores->num_entries; i++)
10424     putFile32BitBE(file, scores->entry[i].time);
10425 }
10426
10427 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10428 {
10429   int i, j;
10430
10431   for (i = 0; i < scores->num_entries; i++)
10432   {
10433     int size = strlen(scores->entry[i].tape_basename);
10434
10435     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10436       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10437   }
10438 }
10439
10440 static void SaveScoreToFilename(char *filename)
10441 {
10442   FILE *file;
10443   int info_chunk_size;
10444   int name_chunk_size;
10445   int scor_chunk_size;
10446   int sc4r_chunk_size;
10447   int time_chunk_size;
10448   int tape_chunk_size;
10449   boolean has_large_score_values;
10450   int i;
10451
10452   if (!(file = fopen(filename, MODE_WRITE)))
10453   {
10454     Warn("cannot save score file '%s'", filename);
10455
10456     return;
10457   }
10458
10459   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10460   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10461   scor_chunk_size = scores.num_entries * 2;
10462   sc4r_chunk_size = scores.num_entries * 4;
10463   time_chunk_size = scores.num_entries * 4;
10464   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10465
10466   has_large_score_values = FALSE;
10467   for (i = 0; i < scores.num_entries; i++)
10468     if (scores.entry[i].score > 0xffff)
10469       has_large_score_values = TRUE;
10470
10471   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10472   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10473
10474   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10475   SaveScore_VERS(file, &scores);
10476
10477   putFileChunkBE(file, "INFO", info_chunk_size);
10478   SaveScore_INFO(file, &scores);
10479
10480   putFileChunkBE(file, "NAME", name_chunk_size);
10481   SaveScore_NAME(file, &scores);
10482
10483   if (has_large_score_values)
10484   {
10485     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10486     SaveScore_SC4R(file, &scores);
10487   }
10488   else
10489   {
10490     putFileChunkBE(file, "SCOR", scor_chunk_size);
10491     SaveScore_SCOR(file, &scores);
10492   }
10493
10494   putFileChunkBE(file, "TIME", time_chunk_size);
10495   SaveScore_TIME(file, &scores);
10496
10497   putFileChunkBE(file, "TAPE", tape_chunk_size);
10498   SaveScore_TAPE(file, &scores);
10499
10500   fclose(file);
10501
10502   SetFilePermissions(filename, PERMS_PRIVATE);
10503 }
10504
10505 void SaveScore(int nr)
10506 {
10507   char *filename = getScoreFilename(nr);
10508   int i;
10509
10510   // used instead of "leveldir_current->subdir" (for network games)
10511   InitScoreDirectory(levelset.identifier);
10512
10513   scores.file_version = FILE_VERSION_ACTUAL;
10514   scores.game_version = GAME_VERSION_ACTUAL;
10515
10516   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10517   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10518   scores.level_nr = level_nr;
10519
10520   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10521     if (scores.entry[i].score == 0 &&
10522         scores.entry[i].time == 0 &&
10523         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10524       break;
10525
10526   scores.num_entries = i;
10527
10528   if (scores.num_entries == 0)
10529     return;
10530
10531   SaveScoreToFilename(filename);
10532 }
10533
10534 static void LoadServerScoreFromCache(int nr)
10535 {
10536   struct ScoreEntry score_entry;
10537   struct
10538   {
10539     void *value;
10540     boolean is_string;
10541     int string_size;
10542   }
10543   score_mapping[] =
10544   {
10545     { &score_entry.score,               FALSE,  0                       },
10546     { &score_entry.time,                FALSE,  0                       },
10547     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10548     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10549     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10550     { &score_entry.id,                  FALSE,  0                       },
10551     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10552     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10553     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10554     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10555
10556     { NULL,                             FALSE,  0                       }
10557   };
10558   char *filename = getScoreCacheFilename(nr);
10559   SetupFileHash *score_hash = loadSetupFileHash(filename);
10560   int i, j;
10561
10562   server_scores.num_entries = 0;
10563
10564   if (score_hash == NULL)
10565     return;
10566
10567   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10568   {
10569     score_entry = server_scores.entry[i];
10570
10571     for (j = 0; score_mapping[j].value != NULL; j++)
10572     {
10573       char token[10];
10574
10575       sprintf(token, "%02d.%d", i, j);
10576
10577       char *value = getHashEntry(score_hash, token);
10578
10579       if (value == NULL)
10580         continue;
10581
10582       if (score_mapping[j].is_string)
10583       {
10584         char *score_value = (char *)score_mapping[j].value;
10585         int value_size = score_mapping[j].string_size;
10586
10587         strncpy(score_value, value, value_size);
10588         score_value[value_size] = '\0';
10589       }
10590       else
10591       {
10592         int *score_value = (int *)score_mapping[j].value;
10593
10594         *score_value = atoi(value);
10595       }
10596
10597       server_scores.num_entries = i + 1;
10598     }
10599
10600     server_scores.entry[i] = score_entry;
10601   }
10602
10603   freeSetupFileHash(score_hash);
10604 }
10605
10606 void LoadServerScore(int nr, boolean download_score)
10607 {
10608   if (!setup.use_api_server)
10609     return;
10610
10611   // always start with reliable default values
10612   setServerScoreInfoToDefaults();
10613
10614   // 1st step: load server scores from cache file (which may not exist)
10615   // (this should prevent reading it while the thread is writing to it)
10616   LoadServerScoreFromCache(nr);
10617
10618   if (download_score && runtime.use_api_server)
10619   {
10620     // 2nd step: download server scores from score server to cache file
10621     // (as thread, as it might time out if the server is not reachable)
10622     ApiGetScoreAsThread(nr);
10623   }
10624 }
10625
10626 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10627 {
10628   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10629
10630   // if score tape not uploaded, ask for uploading missing tapes later
10631   if (!setup.has_remaining_tapes)
10632     setup.ask_for_remaining_tapes = TRUE;
10633
10634   setup.provide_uploading_tapes = TRUE;
10635   setup.has_remaining_tapes = TRUE;
10636
10637   SaveSetup_ServerSetup();
10638 }
10639
10640 void SaveServerScore(int nr, boolean tape_saved)
10641 {
10642   if (!runtime.use_api_server)
10643   {
10644     PrepareScoreTapesForUpload(leveldir_current->subdir);
10645
10646     return;
10647   }
10648
10649   ApiAddScoreAsThread(nr, tape_saved, NULL);
10650 }
10651
10652 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10653                              char *score_tape_filename)
10654 {
10655   if (!runtime.use_api_server)
10656     return;
10657
10658   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10659 }
10660
10661 void LoadLocalAndServerScore(int nr, boolean download_score)
10662 {
10663   int last_added_local = scores.last_added_local;
10664   boolean force_last_added = scores.force_last_added;
10665
10666   // needed if only showing server scores
10667   setScoreInfoToDefaults();
10668
10669   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10670     LoadScore(nr);
10671
10672   // restore last added local score entry (before merging server scores)
10673   scores.last_added = scores.last_added_local = last_added_local;
10674
10675   if (setup.use_api_server &&
10676       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10677   {
10678     // load server scores from cache file and trigger update from server
10679     LoadServerScore(nr, download_score);
10680
10681     // merge local scores with scores from server
10682     MergeServerScore();
10683   }
10684
10685   if (force_last_added)
10686     scores.force_last_added = force_last_added;
10687 }
10688
10689
10690 // ============================================================================
10691 // setup file functions
10692 // ============================================================================
10693
10694 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10695
10696
10697 static struct TokenInfo global_setup_tokens[] =
10698 {
10699   {
10700     TYPE_STRING,
10701     &setup.player_name,                         "player_name"
10702   },
10703   {
10704     TYPE_SWITCH,
10705     &setup.multiple_users,                      "multiple_users"
10706   },
10707   {
10708     TYPE_SWITCH,
10709     &setup.sound,                               "sound"
10710   },
10711   {
10712     TYPE_SWITCH,
10713     &setup.sound_loops,                         "repeating_sound_loops"
10714   },
10715   {
10716     TYPE_SWITCH,
10717     &setup.sound_music,                         "background_music"
10718   },
10719   {
10720     TYPE_SWITCH,
10721     &setup.sound_simple,                        "simple_sound_effects"
10722   },
10723   {
10724     TYPE_SWITCH,
10725     &setup.toons,                               "toons"
10726   },
10727   {
10728     TYPE_SWITCH,
10729     &setup.global_animations,                   "global_animations"
10730   },
10731   {
10732     TYPE_SWITCH,
10733     &setup.scroll_delay,                        "scroll_delay"
10734   },
10735   {
10736     TYPE_SWITCH,
10737     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10738   },
10739   {
10740     TYPE_INTEGER,
10741     &setup.scroll_delay_value,                  "scroll_delay_value"
10742   },
10743   {
10744     TYPE_STRING,
10745     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10746   },
10747   {
10748     TYPE_INTEGER,
10749     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10750   },
10751   {
10752     TYPE_SWITCH,
10753     &setup.fade_screens,                        "fade_screens"
10754   },
10755   {
10756     TYPE_SWITCH,
10757     &setup.autorecord,                          "automatic_tape_recording"
10758   },
10759   {
10760     TYPE_SWITCH,
10761     &setup.autorecord_after_replay,             "autorecord_after_replay"
10762   },
10763   {
10764     TYPE_SWITCH,
10765     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10766   },
10767   {
10768     TYPE_SWITCH,
10769     &setup.show_titlescreen,                    "show_titlescreen"
10770   },
10771   {
10772     TYPE_SWITCH,
10773     &setup.quick_doors,                         "quick_doors"
10774   },
10775   {
10776     TYPE_SWITCH,
10777     &setup.team_mode,                           "team_mode"
10778   },
10779   {
10780     TYPE_SWITCH,
10781     &setup.handicap,                            "handicap"
10782   },
10783   {
10784     TYPE_SWITCH,
10785     &setup.skip_levels,                         "skip_levels"
10786   },
10787   {
10788     TYPE_SWITCH_3_STATES,
10789     &setup.allow_skipping_levels,               "allow_skipping_levels"
10790   },
10791   {
10792     TYPE_SWITCH,
10793     &setup.increment_levels,                    "increment_levels"
10794   },
10795   {
10796     TYPE_SWITCH,
10797     &setup.auto_play_next_level,                "auto_play_next_level"
10798   },
10799   {
10800     TYPE_SWITCH,
10801     &setup.count_score_after_game,              "count_score_after_game"
10802   },
10803   {
10804     TYPE_SWITCH,
10805     &setup.show_scores_after_game,              "show_scores_after_game"
10806   },
10807   {
10808     TYPE_SWITCH,
10809     &setup.time_limit,                          "time_limit"
10810   },
10811   {
10812     TYPE_SWITCH,
10813     &setup.fullscreen,                          "fullscreen"
10814   },
10815   {
10816     TYPE_INTEGER,
10817     &setup.window_scaling_percent,              "window_scaling_percent"
10818   },
10819   {
10820     TYPE_STRING,
10821     &setup.window_scaling_quality,              "window_scaling_quality"
10822   },
10823   {
10824     TYPE_STRING,
10825     &setup.screen_rendering_mode,               "screen_rendering_mode"
10826   },
10827   {
10828     TYPE_STRING,
10829     &setup.vsync_mode,                          "vsync_mode"
10830   },
10831   {
10832     TYPE_SWITCH,
10833     &setup.ask_on_escape,                       "ask_on_escape"
10834   },
10835   {
10836     TYPE_SWITCH,
10837     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10838   },
10839   {
10840     TYPE_SWITCH,
10841     &setup.ask_on_game_over,                    "ask_on_game_over"
10842   },
10843   {
10844     TYPE_SWITCH,
10845     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10846   },
10847   {
10848     TYPE_SWITCH,
10849     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10850   },
10851   {
10852     TYPE_SWITCH,
10853     &setup.quick_switch,                        "quick_player_switch"
10854   },
10855   {
10856     TYPE_SWITCH,
10857     &setup.input_on_focus,                      "input_on_focus"
10858   },
10859   {
10860     TYPE_SWITCH,
10861     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10862   },
10863   {
10864     TYPE_SWITCH,
10865     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10866   },
10867   {
10868     TYPE_SWITCH,
10869     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10870   },
10871   {
10872     TYPE_SWITCH,
10873     &setup.game_speed_extended,                 "game_speed_extended"
10874   },
10875   {
10876     TYPE_INTEGER,
10877     &setup.game_frame_delay,                    "game_frame_delay"
10878   },
10879   {
10880     TYPE_INTEGER,
10881     &setup.default_game_engine_type,            "default_game_engine_type"
10882   },
10883   {
10884     TYPE_SWITCH,
10885     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10886   },
10887   {
10888     TYPE_SWITCH,
10889     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10890   },
10891   {
10892     TYPE_SWITCH,
10893     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10894   },
10895   {
10896     TYPE_SWITCH,
10897     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10898   },
10899   {
10900     TYPE_SWITCH_3_STATES,
10901     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10902   },
10903   {
10904     TYPE_SWITCH_3_STATES,
10905     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10906   },
10907   {
10908     TYPE_SWITCH_3_STATES,
10909     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10910   },
10911   {
10912     TYPE_SWITCH_3_STATES,
10913     &setup.bd_falling_sounds,                   "bd_falling_sounds"
10914   },
10915   {
10916     TYPE_INTEGER,
10917     &setup.bd_palette_c64,                      "bd_palette_c64"
10918   },
10919   {
10920     TYPE_INTEGER,
10921     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10922   },
10923   {
10924     TYPE_INTEGER,
10925     &setup.bd_palette_atari,                    "bd_palette_atari"
10926   },
10927   {
10928     TYPE_INTEGER,
10929     &setup.bd_default_color_type,               "bd_default_color_type"
10930   },
10931   {
10932     TYPE_SWITCH,
10933     &setup.bd_random_colors,                    "bd_random_colors"
10934   },
10935   {
10936     TYPE_SWITCH,
10937     &setup.sp_show_border_elements,             "sp_show_border_elements"
10938   },
10939   {
10940     TYPE_SWITCH,
10941     &setup.small_game_graphics,                 "small_game_graphics"
10942   },
10943   {
10944     TYPE_SWITCH,
10945     &setup.show_load_save_buttons,              "show_load_save_buttons"
10946   },
10947   {
10948     TYPE_SWITCH,
10949     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10950   },
10951   {
10952     TYPE_STRING,
10953     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10954   },
10955   {
10956     TYPE_STRING,
10957     &setup.graphics_set,                        "graphics_set"
10958   },
10959   {
10960     TYPE_STRING,
10961     &setup.sounds_set,                          "sounds_set"
10962   },
10963   {
10964     TYPE_STRING,
10965     &setup.music_set,                           "music_set"
10966   },
10967   {
10968     TYPE_SWITCH_3_STATES,
10969     &setup.override_level_graphics,             "override_level_graphics"
10970   },
10971   {
10972     TYPE_SWITCH_3_STATES,
10973     &setup.override_level_sounds,               "override_level_sounds"
10974   },
10975   {
10976     TYPE_SWITCH_3_STATES,
10977     &setup.override_level_music,                "override_level_music"
10978   },
10979   {
10980     TYPE_INTEGER,
10981     &setup.volume_simple,                       "volume_simple"
10982   },
10983   {
10984     TYPE_INTEGER,
10985     &setup.volume_loops,                        "volume_loops"
10986   },
10987   {
10988     TYPE_INTEGER,
10989     &setup.volume_music,                        "volume_music"
10990   },
10991   {
10992     TYPE_SWITCH,
10993     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10994   },
10995   {
10996     TYPE_SWITCH,
10997     &setup.network_mode,                        "network_mode"
10998   },
10999   {
11000     TYPE_PLAYER,
11001     &setup.network_player_nr,                   "network_player"
11002   },
11003   {
11004     TYPE_STRING,
11005     &setup.network_server_hostname,             "network_server_hostname"
11006   },
11007   {
11008     TYPE_STRING,
11009     &setup.touch.control_type,                  "touch.control_type"
11010   },
11011   {
11012     TYPE_INTEGER,
11013     &setup.touch.move_distance,                 "touch.move_distance"
11014   },
11015   {
11016     TYPE_INTEGER,
11017     &setup.touch.drop_distance,                 "touch.drop_distance"
11018   },
11019   {
11020     TYPE_INTEGER,
11021     &setup.touch.transparency,                  "touch.transparency"
11022   },
11023   {
11024     TYPE_INTEGER,
11025     &setup.touch.draw_outlined,                 "touch.draw_outlined"
11026   },
11027   {
11028     TYPE_INTEGER,
11029     &setup.touch.draw_pressed,                  "touch.draw_pressed"
11030   },
11031   {
11032     TYPE_INTEGER,
11033     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
11034   },
11035   {
11036     TYPE_INTEGER,
11037     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
11038   },
11039   {
11040     TYPE_INTEGER,
11041     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
11042   },
11043   {
11044     TYPE_INTEGER,
11045     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
11046   },
11047   {
11048     TYPE_SWITCH,
11049     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
11050   },
11051 };
11052
11053 static struct TokenInfo auto_setup_tokens[] =
11054 {
11055   {
11056     TYPE_INTEGER,
11057     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
11058   },
11059 };
11060
11061 static struct TokenInfo server_setup_tokens[] =
11062 {
11063   {
11064     TYPE_STRING,
11065     &setup.player_uuid,                         "player_uuid"
11066   },
11067   {
11068     TYPE_INTEGER,
11069     &setup.player_version,                      "player_version"
11070   },
11071   {
11072     TYPE_SWITCH,
11073     &setup.use_api_server,          TEST_PREFIX "use_api_server"
11074   },
11075   {
11076     TYPE_STRING,
11077     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
11078   },
11079   {
11080     TYPE_STRING,
11081     &setup.api_server_password,     TEST_PREFIX "api_server_password"
11082   },
11083   {
11084     TYPE_SWITCH,
11085     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11086   },
11087   {
11088     TYPE_SWITCH,
11089     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11090   },
11091   {
11092     TYPE_SWITCH,
11093     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11094   },
11095   {
11096     TYPE_SWITCH,
11097     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11098   },
11099   {
11100     TYPE_SWITCH,
11101     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
11102   },
11103 };
11104
11105 static struct TokenInfo editor_setup_tokens[] =
11106 {
11107   {
11108     TYPE_SWITCH,
11109     &setup.editor.el_classic,                   "editor.el_classic"
11110   },
11111   {
11112     TYPE_SWITCH,
11113     &setup.editor.el_custom,                    "editor.el_custom"
11114   },
11115   {
11116     TYPE_SWITCH,
11117     &setup.editor.el_user_defined,              "editor.el_user_defined"
11118   },
11119   {
11120     TYPE_SWITCH,
11121     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11122   },
11123   {
11124     TYPE_SWITCH,
11125     &setup.editor.el_headlines,                 "editor.el_headlines"
11126   },
11127   {
11128     TYPE_SWITCH,
11129     &setup.editor.show_element_token,           "editor.show_element_token"
11130   },
11131   {
11132     TYPE_SWITCH,
11133     &setup.editor.fast_game_start,              "editor.fast_game_start"
11134   },
11135   {
11136     TYPE_SWITCH,
11137     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11138   },
11139 };
11140
11141 static struct TokenInfo editor_cascade_setup_tokens[] =
11142 {
11143   {
11144     TYPE_SWITCH,
11145     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11146   },
11147   {
11148     TYPE_SWITCH,
11149     &setup.editor_cascade.el_bdx,               "editor.cascade.el_bdx"
11150   },
11151   {
11152     TYPE_SWITCH,
11153     &setup.editor_cascade.el_bdx_effects,       "editor.cascade.el_bdx_effects"
11154   },
11155   {
11156     TYPE_SWITCH,
11157     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11158   },
11159   {
11160     TYPE_SWITCH,
11161     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11162   },
11163   {
11164     TYPE_SWITCH,
11165     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11166   },
11167   {
11168     TYPE_SWITCH,
11169     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11170   },
11171   {
11172     TYPE_SWITCH,
11173     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11174   },
11175   {
11176     TYPE_SWITCH,
11177     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11178   },
11179   {
11180     TYPE_SWITCH,
11181     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11182   },
11183   {
11184     TYPE_SWITCH,
11185     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11186   },
11187   {
11188     TYPE_SWITCH,
11189     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11190   },
11191   {
11192     TYPE_SWITCH,
11193     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11194   },
11195   {
11196     TYPE_SWITCH,
11197     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11198   },
11199   {
11200     TYPE_SWITCH,
11201     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11202   },
11203   {
11204     TYPE_SWITCH,
11205     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11206   },
11207   {
11208     TYPE_SWITCH,
11209     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11210   },
11211   {
11212     TYPE_SWITCH,
11213     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11214   },
11215   {
11216     TYPE_SWITCH,
11217     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11218   },
11219   {
11220     TYPE_SWITCH,
11221     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11222   },
11223 };
11224
11225 static struct TokenInfo shortcut_setup_tokens[] =
11226 {
11227   {
11228     TYPE_KEY_X11,
11229     &setup.shortcut.save_game,                  "shortcut.save_game"
11230   },
11231   {
11232     TYPE_KEY_X11,
11233     &setup.shortcut.load_game,                  "shortcut.load_game"
11234   },
11235   {
11236     TYPE_KEY_X11,
11237     &setup.shortcut.restart_game,               "shortcut.restart_game"
11238   },
11239   {
11240     TYPE_KEY_X11,
11241     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11242   },
11243   {
11244     TYPE_KEY_X11,
11245     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11246   },
11247   {
11248     TYPE_KEY_X11,
11249     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11250   },
11251   {
11252     TYPE_KEY_X11,
11253     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11254   },
11255   {
11256     TYPE_KEY_X11,
11257     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11258   },
11259   {
11260     TYPE_KEY_X11,
11261     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11262   },
11263   {
11264     TYPE_KEY_X11,
11265     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11266   },
11267   {
11268     TYPE_KEY_X11,
11269     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11270   },
11271   {
11272     TYPE_KEY_X11,
11273     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11274   },
11275   {
11276     TYPE_KEY_X11,
11277     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11278   },
11279   {
11280     TYPE_KEY_X11,
11281     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11282   },
11283   {
11284     TYPE_KEY_X11,
11285     &setup.shortcut.tape_record,                "shortcut.tape_record"
11286   },
11287   {
11288     TYPE_KEY_X11,
11289     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11290   },
11291   {
11292     TYPE_KEY_X11,
11293     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11294   },
11295   {
11296     TYPE_KEY_X11,
11297     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11298   },
11299   {
11300     TYPE_KEY_X11,
11301     &setup.shortcut.sound_music,                "shortcut.sound_music"
11302   },
11303   {
11304     TYPE_KEY_X11,
11305     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11306   },
11307   {
11308     TYPE_KEY_X11,
11309     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11310   },
11311   {
11312     TYPE_KEY_X11,
11313     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11314   },
11315   {
11316     TYPE_KEY_X11,
11317     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11318   },
11319   {
11320     TYPE_KEY_X11,
11321     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11322   },
11323   {
11324     TYPE_KEY_X11,
11325     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11326   },
11327 };
11328
11329 static struct SetupInputInfo setup_input;
11330 static struct TokenInfo player_setup_tokens[] =
11331 {
11332   {
11333     TYPE_BOOLEAN,
11334     &setup_input.use_joystick,                  ".use_joystick"
11335   },
11336   {
11337     TYPE_STRING,
11338     &setup_input.joy.device_name,               ".joy.device_name"
11339   },
11340   {
11341     TYPE_INTEGER,
11342     &setup_input.joy.xleft,                     ".joy.xleft"
11343   },
11344   {
11345     TYPE_INTEGER,
11346     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11347   },
11348   {
11349     TYPE_INTEGER,
11350     &setup_input.joy.xright,                    ".joy.xright"
11351   },
11352   {
11353     TYPE_INTEGER,
11354     &setup_input.joy.yupper,                    ".joy.yupper"
11355   },
11356   {
11357     TYPE_INTEGER,
11358     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11359   },
11360   {
11361     TYPE_INTEGER,
11362     &setup_input.joy.ylower,                    ".joy.ylower"
11363   },
11364   {
11365     TYPE_INTEGER,
11366     &setup_input.joy.snap,                      ".joy.snap_field"
11367   },
11368   {
11369     TYPE_INTEGER,
11370     &setup_input.joy.drop,                      ".joy.place_bomb"
11371   },
11372   {
11373     TYPE_KEY_X11,
11374     &setup_input.key.left,                      ".key.move_left"
11375   },
11376   {
11377     TYPE_KEY_X11,
11378     &setup_input.key.right,                     ".key.move_right"
11379   },
11380   {
11381     TYPE_KEY_X11,
11382     &setup_input.key.up,                        ".key.move_up"
11383   },
11384   {
11385     TYPE_KEY_X11,
11386     &setup_input.key.down,                      ".key.move_down"
11387   },
11388   {
11389     TYPE_KEY_X11,
11390     &setup_input.key.snap,                      ".key.snap_field"
11391   },
11392   {
11393     TYPE_KEY_X11,
11394     &setup_input.key.drop,                      ".key.place_bomb"
11395   },
11396 };
11397
11398 static struct TokenInfo system_setup_tokens[] =
11399 {
11400   {
11401     TYPE_STRING,
11402     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11403   },
11404   {
11405     TYPE_STRING,
11406     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11407   },
11408   {
11409     TYPE_STRING,
11410     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11411   },
11412   {
11413     TYPE_INTEGER,
11414     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11415   },
11416 };
11417
11418 static struct TokenInfo internal_setup_tokens[] =
11419 {
11420   {
11421     TYPE_STRING,
11422     &setup.internal.program_title,              "program_title"
11423   },
11424   {
11425     TYPE_STRING,
11426     &setup.internal.program_version,            "program_version"
11427   },
11428   {
11429     TYPE_STRING,
11430     &setup.internal.program_author,             "program_author"
11431   },
11432   {
11433     TYPE_STRING,
11434     &setup.internal.program_email,              "program_email"
11435   },
11436   {
11437     TYPE_STRING,
11438     &setup.internal.program_website,            "program_website"
11439   },
11440   {
11441     TYPE_STRING,
11442     &setup.internal.program_copyright,          "program_copyright"
11443   },
11444   {
11445     TYPE_STRING,
11446     &setup.internal.program_company,            "program_company"
11447   },
11448   {
11449     TYPE_STRING,
11450     &setup.internal.program_icon_file,          "program_icon_file"
11451   },
11452   {
11453     TYPE_STRING,
11454     &setup.internal.default_graphics_set,       "default_graphics_set"
11455   },
11456   {
11457     TYPE_STRING,
11458     &setup.internal.default_sounds_set,         "default_sounds_set"
11459   },
11460   {
11461     TYPE_STRING,
11462     &setup.internal.default_music_set,          "default_music_set"
11463   },
11464   {
11465     TYPE_STRING,
11466     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11467   },
11468   {
11469     TYPE_STRING,
11470     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11471   },
11472   {
11473     TYPE_STRING,
11474     &setup.internal.fallback_music_file,        "fallback_music_file"
11475   },
11476   {
11477     TYPE_STRING,
11478     &setup.internal.default_level_series,       "default_level_series"
11479   },
11480   {
11481     TYPE_INTEGER,
11482     &setup.internal.default_window_width,       "default_window_width"
11483   },
11484   {
11485     TYPE_INTEGER,
11486     &setup.internal.default_window_height,      "default_window_height"
11487   },
11488   {
11489     TYPE_BOOLEAN,
11490     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11491   },
11492   {
11493     TYPE_BOOLEAN,
11494     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11495   },
11496   {
11497     TYPE_BOOLEAN,
11498     &setup.internal.create_user_levelset,       "create_user_levelset"
11499   },
11500   {
11501     TYPE_BOOLEAN,
11502     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11503   },
11504   {
11505     TYPE_BOOLEAN,
11506     &setup.internal.menu_game,                  "menu_game"
11507   },
11508   {
11509     TYPE_BOOLEAN,
11510     &setup.internal.menu_engines,               "menu_engines"
11511   },
11512   {
11513     TYPE_BOOLEAN,
11514     &setup.internal.menu_editor,                "menu_editor"
11515   },
11516   {
11517     TYPE_BOOLEAN,
11518     &setup.internal.menu_graphics,              "menu_graphics"
11519   },
11520   {
11521     TYPE_BOOLEAN,
11522     &setup.internal.menu_sound,                 "menu_sound"
11523   },
11524   {
11525     TYPE_BOOLEAN,
11526     &setup.internal.menu_artwork,               "menu_artwork"
11527   },
11528   {
11529     TYPE_BOOLEAN,
11530     &setup.internal.menu_input,                 "menu_input"
11531   },
11532   {
11533     TYPE_BOOLEAN,
11534     &setup.internal.menu_touch,                 "menu_touch"
11535   },
11536   {
11537     TYPE_BOOLEAN,
11538     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11539   },
11540   {
11541     TYPE_BOOLEAN,
11542     &setup.internal.menu_exit,                  "menu_exit"
11543   },
11544   {
11545     TYPE_BOOLEAN,
11546     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11547   },
11548   {
11549     TYPE_BOOLEAN,
11550     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11551   },
11552   {
11553     TYPE_BOOLEAN,
11554     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11555   },
11556   {
11557     TYPE_BOOLEAN,
11558     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11559   },
11560   {
11561     TYPE_BOOLEAN,
11562     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11563   },
11564   {
11565     TYPE_BOOLEAN,
11566     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11567   },
11568   {
11569     TYPE_BOOLEAN,
11570     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11571   },
11572   {
11573     TYPE_BOOLEAN,
11574     &setup.internal.info_title,                 "info_title"
11575   },
11576   {
11577     TYPE_BOOLEAN,
11578     &setup.internal.info_elements,              "info_elements"
11579   },
11580   {
11581     TYPE_BOOLEAN,
11582     &setup.internal.info_music,                 "info_music"
11583   },
11584   {
11585     TYPE_BOOLEAN,
11586     &setup.internal.info_credits,               "info_credits"
11587   },
11588   {
11589     TYPE_BOOLEAN,
11590     &setup.internal.info_program,               "info_program"
11591   },
11592   {
11593     TYPE_BOOLEAN,
11594     &setup.internal.info_version,               "info_version"
11595   },
11596   {
11597     TYPE_BOOLEAN,
11598     &setup.internal.info_levelset,              "info_levelset"
11599   },
11600   {
11601     TYPE_BOOLEAN,
11602     &setup.internal.info_exit,                  "info_exit"
11603   },
11604 };
11605
11606 static struct TokenInfo debug_setup_tokens[] =
11607 {
11608   {
11609     TYPE_INTEGER,
11610     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11611   },
11612   {
11613     TYPE_INTEGER,
11614     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11615   },
11616   {
11617     TYPE_INTEGER,
11618     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11619   },
11620   {
11621     TYPE_INTEGER,
11622     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11623   },
11624   {
11625     TYPE_INTEGER,
11626     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11627   },
11628   {
11629     TYPE_INTEGER,
11630     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11631   },
11632   {
11633     TYPE_INTEGER,
11634     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11635   },
11636   {
11637     TYPE_INTEGER,
11638     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11639   },
11640   {
11641     TYPE_INTEGER,
11642     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11643   },
11644   {
11645     TYPE_INTEGER,
11646     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11647   },
11648   {
11649     TYPE_KEY_X11,
11650     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11651   },
11652   {
11653     TYPE_KEY_X11,
11654     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11655   },
11656   {
11657     TYPE_KEY_X11,
11658     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11659   },
11660   {
11661     TYPE_KEY_X11,
11662     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11663   },
11664   {
11665     TYPE_KEY_X11,
11666     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11667   },
11668   {
11669     TYPE_KEY_X11,
11670     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11671   },
11672   {
11673     TYPE_KEY_X11,
11674     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11675   },
11676   {
11677     TYPE_KEY_X11,
11678     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11679   },
11680   {
11681     TYPE_KEY_X11,
11682     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11683   },
11684   {
11685     TYPE_KEY_X11,
11686     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11687   },
11688   {
11689     TYPE_BOOLEAN,
11690     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11691   {
11692     TYPE_BOOLEAN,
11693     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11694   },
11695   {
11696     TYPE_BOOLEAN,
11697     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11698   },
11699   {
11700     TYPE_SWITCH_3_STATES,
11701     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11702   },
11703   {
11704     TYPE_INTEGER,
11705     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11706   },
11707 };
11708
11709 static struct TokenInfo options_setup_tokens[] =
11710 {
11711   {
11712     TYPE_BOOLEAN,
11713     &setup.options.verbose,                     "options.verbose"
11714   },
11715   {
11716     TYPE_BOOLEAN,
11717     &setup.options.debug,                       "options.debug"
11718   },
11719   {
11720     TYPE_STRING,
11721     &setup.options.debug_mode,                  "options.debug_mode"
11722   },
11723 };
11724
11725 static void setSetupInfoToDefaults(struct SetupInfo *si)
11726 {
11727   int i;
11728
11729   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11730
11731   si->multiple_users = TRUE;
11732
11733   si->sound = TRUE;
11734   si->sound_loops = TRUE;
11735   si->sound_music = TRUE;
11736   si->sound_simple = TRUE;
11737   si->toons = TRUE;
11738   si->global_animations = TRUE;
11739   si->scroll_delay = TRUE;
11740   si->forced_scroll_delay = FALSE;
11741   si->scroll_delay_value = STD_SCROLL_DELAY;
11742   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11743   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11744   si->fade_screens = TRUE;
11745   si->autorecord = TRUE;
11746   si->autorecord_after_replay = TRUE;
11747   si->auto_pause_on_start = FALSE;
11748   si->show_titlescreen = TRUE;
11749   si->quick_doors = FALSE;
11750   si->team_mode = FALSE;
11751   si->handicap = TRUE;
11752   si->skip_levels = TRUE;
11753   si->allow_skipping_levels = STATE_ASK;
11754   si->increment_levels = TRUE;
11755   si->auto_play_next_level = TRUE;
11756   si->count_score_after_game = TRUE;
11757   si->show_scores_after_game = TRUE;
11758   si->time_limit = TRUE;
11759   si->fullscreen = FALSE;
11760   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11761   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11762   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11763   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11764   si->ask_on_escape = TRUE;
11765   si->ask_on_escape_editor = TRUE;
11766   si->ask_on_game_over = TRUE;
11767   si->ask_on_quit_game = TRUE;
11768   si->ask_on_quit_program = TRUE;
11769   si->quick_switch = FALSE;
11770   si->input_on_focus = FALSE;
11771   si->prefer_aga_graphics = TRUE;
11772   si->prefer_lowpass_sounds = FALSE;
11773   si->prefer_extra_panel_items = TRUE;
11774   si->game_speed_extended = FALSE;
11775   si->game_frame_delay = GAME_FRAME_DELAY;
11776   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11777   si->bd_skip_uncovering = FALSE;
11778   si->bd_skip_hatching = FALSE;
11779   si->bd_scroll_delay = TRUE;
11780   si->bd_show_invisible_outbox = FALSE;
11781   si->bd_smooth_movements = STATE_TRUE;
11782   si->bd_pushing_graphics = STATE_TRUE;
11783   si->bd_up_down_graphics = STATE_TRUE;
11784   si->bd_falling_sounds = STATE_AUTO;
11785   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11786   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11787   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11788   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11789   si->bd_random_colors = FALSE;
11790   si->sp_show_border_elements = FALSE;
11791   si->small_game_graphics = FALSE;
11792   si->show_load_save_buttons = FALSE;
11793   si->show_undo_redo_buttons = FALSE;
11794   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11795
11796   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11797   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11798   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11799
11800   si->override_level_graphics = STATE_FALSE;
11801   si->override_level_sounds = STATE_FALSE;
11802   si->override_level_music = STATE_FALSE;
11803
11804   si->volume_simple = 100;              // percent
11805   si->volume_loops = 100;               // percent
11806   si->volume_music = 100;               // percent
11807   si->audio_sample_rate_44100 = FALSE;
11808
11809   si->network_mode = FALSE;
11810   si->network_player_nr = 0;            // first player
11811   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11812
11813   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11814   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11815   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11816   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11817   si->touch.draw_outlined = TRUE;
11818   si->touch.draw_pressed = TRUE;
11819
11820   for (i = 0; i < 2; i++)
11821   {
11822     char *default_grid_button[6][2] =
11823     {
11824       { "      ", "  ^^  " },
11825       { "      ", "  ^^  " },
11826       { "      ", "<<  >>" },
11827       { "      ", "<<  >>" },
11828       { "111222", "  vv  " },
11829       { "111222", "  vv  " }
11830     };
11831     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11832     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11833     int min_xsize = MIN(6, grid_xsize);
11834     int min_ysize = MIN(6, grid_ysize);
11835     int startx = grid_xsize - min_xsize;
11836     int starty = grid_ysize - min_ysize;
11837     int x, y;
11838
11839     // virtual buttons grid can only be set to defaults if video is initialized
11840     // (this will be repeated if virtual buttons are not loaded from setup file)
11841     if (video.initialized)
11842     {
11843       si->touch.grid_xsize[i] = grid_xsize;
11844       si->touch.grid_ysize[i] = grid_ysize;
11845     }
11846     else
11847     {
11848       si->touch.grid_xsize[i] = -1;
11849       si->touch.grid_ysize[i] = -1;
11850     }
11851
11852     for (x = 0; x < MAX_GRID_XSIZE; x++)
11853       for (y = 0; y < MAX_GRID_YSIZE; y++)
11854         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11855
11856     for (x = 0; x < min_xsize; x++)
11857       for (y = 0; y < min_ysize; y++)
11858         si->touch.grid_button[i][x][starty + y] =
11859           default_grid_button[y][0][x];
11860
11861     for (x = 0; x < min_xsize; x++)
11862       for (y = 0; y < min_ysize; y++)
11863         si->touch.grid_button[i][startx + x][starty + y] =
11864           default_grid_button[y][1][x];
11865   }
11866
11867   si->touch.grid_initialized            = video.initialized;
11868
11869   si->touch.overlay_buttons             = FALSE;
11870
11871   si->editor.el_boulderdash             = TRUE;
11872   si->editor.el_boulderdash_native      = TRUE;
11873   si->editor.el_boulderdash_effects     = TRUE;
11874   si->editor.el_emerald_mine            = TRUE;
11875   si->editor.el_emerald_mine_club       = TRUE;
11876   si->editor.el_more                    = TRUE;
11877   si->editor.el_sokoban                 = TRUE;
11878   si->editor.el_supaplex                = TRUE;
11879   si->editor.el_diamond_caves           = TRUE;
11880   si->editor.el_dx_boulderdash          = TRUE;
11881
11882   si->editor.el_mirror_magic            = TRUE;
11883   si->editor.el_deflektor               = TRUE;
11884
11885   si->editor.el_chars                   = TRUE;
11886   si->editor.el_steel_chars             = TRUE;
11887
11888   si->editor.el_classic                 = TRUE;
11889   si->editor.el_custom                  = TRUE;
11890
11891   si->editor.el_user_defined            = FALSE;
11892   si->editor.el_dynamic                 = TRUE;
11893
11894   si->editor.el_headlines               = TRUE;
11895
11896   si->editor.show_element_token         = FALSE;
11897   si->editor.fast_game_start            = FALSE;
11898
11899   si->editor.show_read_only_warning     = TRUE;
11900
11901   si->editor.use_template_for_new_levels = TRUE;
11902
11903   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11904   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11905   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11906   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11907   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11908
11909   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11910   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11911   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11912   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11913   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11914
11915   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11916   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11917   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11918   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11919   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11920   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11921
11922   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11923   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11924   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11925
11926   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11927   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11928   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11929   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11930
11931   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11932   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11933
11934   for (i = 0; i < MAX_PLAYERS; i++)
11935   {
11936     si->input[i].use_joystick = FALSE;
11937     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11938     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11939     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11940     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11941     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11942     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11943     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11944     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11945     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11946     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11947     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11948     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11949     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11950     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11951     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11952   }
11953
11954   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11955   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11956   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11957   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11958
11959   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11960   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11961   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11962   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11963   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11964   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11965   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11966
11967   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11968
11969   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11970   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11971   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11972
11973   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11974   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11975   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11976
11977   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11978   si->internal.choose_from_top_leveldir = FALSE;
11979   si->internal.show_scaling_in_title = TRUE;
11980   si->internal.create_user_levelset = TRUE;
11981   si->internal.info_screens_from_main = FALSE;
11982
11983   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11984   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11985
11986   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11987   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11988   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11989   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11990   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11991   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11992   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11993   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11994   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11995   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11996
11997   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11998   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11999   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
12000   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
12001   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
12002   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
12003   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
12004   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
12005   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
12006   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
12007
12008   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
12009   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
12010
12011   si->debug.show_frames_per_second = FALSE;
12012
12013   si->debug.xsn_mode = STATE_AUTO;
12014   si->debug.xsn_percent = 0;
12015
12016   si->options.verbose = FALSE;
12017   si->options.debug = FALSE;
12018   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
12019
12020 #if defined(PLATFORM_ANDROID)
12021   si->fullscreen = TRUE;
12022   si->touch.overlay_buttons = TRUE;
12023 #endif
12024
12025   setHideSetupEntry(&setup.debug.xsn_mode);
12026 }
12027
12028 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
12029 {
12030   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
12031 }
12032
12033 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
12034 {
12035   si->player_uuid = NULL;       // (will be set later)
12036   si->player_version = 1;       // (will be set later)
12037
12038   si->use_api_server = TRUE;
12039   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
12040   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
12041   si->ask_for_uploading_tapes = TRUE;
12042   si->ask_for_remaining_tapes = FALSE;
12043   si->provide_uploading_tapes = TRUE;
12044   si->ask_for_using_api_server = TRUE;
12045   si->has_remaining_tapes = FALSE;
12046 }
12047
12048 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
12049 {
12050   si->editor_cascade.el_bd              = TRUE;
12051   si->editor_cascade.el_bdx             = TRUE;
12052   si->editor_cascade.el_bdx_effects     = FALSE;
12053   si->editor_cascade.el_em              = TRUE;
12054   si->editor_cascade.el_emc             = TRUE;
12055   si->editor_cascade.el_rnd             = TRUE;
12056   si->editor_cascade.el_sb              = TRUE;
12057   si->editor_cascade.el_sp              = TRUE;
12058   si->editor_cascade.el_dc              = TRUE;
12059   si->editor_cascade.el_dx              = TRUE;
12060
12061   si->editor_cascade.el_mm              = TRUE;
12062   si->editor_cascade.el_df              = TRUE;
12063
12064   si->editor_cascade.el_chars           = FALSE;
12065   si->editor_cascade.el_steel_chars     = FALSE;
12066   si->editor_cascade.el_ce              = FALSE;
12067   si->editor_cascade.el_ge              = FALSE;
12068   si->editor_cascade.el_es              = FALSE;
12069   si->editor_cascade.el_ref             = FALSE;
12070   si->editor_cascade.el_user            = FALSE;
12071   si->editor_cascade.el_dynamic         = FALSE;
12072 }
12073
12074 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
12075
12076 static char *getHideSetupToken(void *setup_value)
12077 {
12078   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12079
12080   if (setup_value != NULL)
12081     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12082
12083   return hide_setup_token;
12084 }
12085
12086 void setHideSetupEntry(void *setup_value)
12087 {
12088   char *hide_setup_token = getHideSetupToken(setup_value);
12089
12090   if (hide_setup_hash == NULL)
12091     hide_setup_hash = newSetupFileHash();
12092
12093   if (setup_value != NULL)
12094     setHashEntry(hide_setup_hash, hide_setup_token, "");
12095 }
12096
12097 void removeHideSetupEntry(void *setup_value)
12098 {
12099   char *hide_setup_token = getHideSetupToken(setup_value);
12100
12101   if (setup_value != NULL)
12102     removeHashEntry(hide_setup_hash, hide_setup_token);
12103 }
12104
12105 boolean hideSetupEntry(void *setup_value)
12106 {
12107   char *hide_setup_token = getHideSetupToken(setup_value);
12108
12109   return (setup_value != NULL &&
12110           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12111 }
12112
12113 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12114                                       struct TokenInfo *token_info,
12115                                       int token_nr, char *token_text)
12116 {
12117   char *token_hide_text = getStringCat2(token_text, ".hide");
12118   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12119
12120   // set the value of this setup option in the setup option structure
12121   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12122
12123   // check if this setup option should be hidden in the setup menu
12124   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12125     setHideSetupEntry(token_info[token_nr].value);
12126
12127   free(token_hide_text);
12128 }
12129
12130 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12131                                       struct TokenInfo *token_info,
12132                                       int token_nr)
12133 {
12134   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12135                             token_info[token_nr].text);
12136 }
12137
12138 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12139 {
12140   int i, pnr;
12141
12142   if (!setup_file_hash)
12143     return;
12144
12145   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12146     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12147
12148   setup.touch.grid_initialized = TRUE;
12149   for (i = 0; i < 2; i++)
12150   {
12151     int grid_xsize = setup.touch.grid_xsize[i];
12152     int grid_ysize = setup.touch.grid_ysize[i];
12153     int x, y;
12154
12155     // if virtual buttons are not loaded from setup file, repeat initializing
12156     // virtual buttons grid with default values later when video is initialized
12157     if (grid_xsize == -1 ||
12158         grid_ysize == -1)
12159     {
12160       setup.touch.grid_initialized = FALSE;
12161
12162       continue;
12163     }
12164
12165     for (y = 0; y < grid_ysize; y++)
12166     {
12167       char token_string[MAX_LINE_LEN];
12168
12169       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12170
12171       char *value_string = getHashEntry(setup_file_hash, token_string);
12172
12173       if (value_string == NULL)
12174         continue;
12175
12176       for (x = 0; x < grid_xsize; x++)
12177       {
12178         char c = value_string[x];
12179
12180         setup.touch.grid_button[i][x][y] =
12181           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12182       }
12183     }
12184   }
12185
12186   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12187     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12188
12189   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12190     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12191
12192   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12193   {
12194     char prefix[30];
12195
12196     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12197
12198     setup_input = setup.input[pnr];
12199     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12200     {
12201       char full_token[100];
12202
12203       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12204       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12205                                 full_token);
12206     }
12207     setup.input[pnr] = setup_input;
12208   }
12209
12210   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12211     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12212
12213   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12214     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12215
12216   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12217     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12218
12219   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12220     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12221
12222   setHideRelatedSetupEntries();
12223 }
12224
12225 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12226 {
12227   int i;
12228
12229   if (!setup_file_hash)
12230     return;
12231
12232   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12233     setSetupInfo(auto_setup_tokens, i,
12234                  getHashEntry(setup_file_hash,
12235                               auto_setup_tokens[i].text));
12236 }
12237
12238 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12239 {
12240   int i;
12241
12242   if (!setup_file_hash)
12243     return;
12244
12245   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12246     setSetupInfo(server_setup_tokens, i,
12247                  getHashEntry(setup_file_hash,
12248                               server_setup_tokens[i].text));
12249 }
12250
12251 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12252 {
12253   int i;
12254
12255   if (!setup_file_hash)
12256     return;
12257
12258   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12259     setSetupInfo(editor_cascade_setup_tokens, i,
12260                  getHashEntry(setup_file_hash,
12261                               editor_cascade_setup_tokens[i].text));
12262 }
12263
12264 void LoadUserNames(void)
12265 {
12266   int last_user_nr = user.nr;
12267   int i;
12268
12269   if (global.user_names != NULL)
12270   {
12271     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12272       checked_free(global.user_names[i]);
12273
12274     checked_free(global.user_names);
12275   }
12276
12277   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12278
12279   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12280   {
12281     user.nr = i;
12282
12283     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12284
12285     if (setup_file_hash)
12286     {
12287       char *player_name = getHashEntry(setup_file_hash, "player_name");
12288
12289       global.user_names[i] = getFixedUserName(player_name);
12290
12291       freeSetupFileHash(setup_file_hash);
12292     }
12293
12294     if (global.user_names[i] == NULL)
12295       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12296   }
12297
12298   user.nr = last_user_nr;
12299 }
12300
12301 void LoadSetupFromFilename(char *filename)
12302 {
12303   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12304
12305   if (setup_file_hash)
12306   {
12307     decodeSetupFileHash_Default(setup_file_hash);
12308
12309     freeSetupFileHash(setup_file_hash);
12310   }
12311   else
12312   {
12313     Debug("setup", "using default setup values");
12314   }
12315 }
12316
12317 static void LoadSetup_SpecialPostProcessing(void)
12318 {
12319   char *player_name_new;
12320
12321   // needed to work around problems with fixed length strings
12322   player_name_new = getFixedUserName(setup.player_name);
12323   free(setup.player_name);
12324   setup.player_name = player_name_new;
12325
12326   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12327   if (setup.scroll_delay == FALSE)
12328   {
12329     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12330     setup.scroll_delay = TRUE;                  // now always "on"
12331   }
12332
12333   // make sure that scroll delay value stays inside valid range
12334   setup.scroll_delay_value =
12335     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12336 }
12337
12338 void LoadSetup_Default(void)
12339 {
12340   char *filename;
12341
12342   // always start with reliable default values
12343   setSetupInfoToDefaults(&setup);
12344
12345   // try to load setup values from default setup file
12346   filename = getDefaultSetupFilename();
12347
12348   if (fileExists(filename))
12349     LoadSetupFromFilename(filename);
12350
12351   // try to load setup values from platform setup file
12352   filename = getPlatformSetupFilename();
12353
12354   if (fileExists(filename))
12355     LoadSetupFromFilename(filename);
12356
12357   // try to load setup values from user setup file
12358   filename = getSetupFilename();
12359
12360   LoadSetupFromFilename(filename);
12361
12362   LoadSetup_SpecialPostProcessing();
12363 }
12364
12365 void LoadSetup_AutoSetup(void)
12366 {
12367   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12368   SetupFileHash *setup_file_hash = NULL;
12369
12370   // always start with reliable default values
12371   setSetupInfoToDefaults_AutoSetup(&setup);
12372
12373   setup_file_hash = loadSetupFileHash(filename);
12374
12375   if (setup_file_hash)
12376   {
12377     decodeSetupFileHash_AutoSetup(setup_file_hash);
12378
12379     freeSetupFileHash(setup_file_hash);
12380   }
12381
12382   free(filename);
12383 }
12384
12385 void LoadSetup_ServerSetup(void)
12386 {
12387   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12388   SetupFileHash *setup_file_hash = NULL;
12389
12390   // always start with reliable default values
12391   setSetupInfoToDefaults_ServerSetup(&setup);
12392
12393   setup_file_hash = loadSetupFileHash(filename);
12394
12395   if (setup_file_hash)
12396   {
12397     decodeSetupFileHash_ServerSetup(setup_file_hash);
12398
12399     freeSetupFileHash(setup_file_hash);
12400   }
12401
12402   free(filename);
12403
12404   if (setup.player_uuid == NULL)
12405   {
12406     // player UUID does not yet exist in setup file
12407     setup.player_uuid = getStringCopy(getUUID());
12408     setup.player_version = 2;
12409
12410     SaveSetup_ServerSetup();
12411   }
12412 }
12413
12414 void LoadSetup_EditorCascade(void)
12415 {
12416   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12417   SetupFileHash *setup_file_hash = NULL;
12418
12419   // always start with reliable default values
12420   setSetupInfoToDefaults_EditorCascade(&setup);
12421
12422   setup_file_hash = loadSetupFileHash(filename);
12423
12424   if (setup_file_hash)
12425   {
12426     decodeSetupFileHash_EditorCascade(setup_file_hash);
12427
12428     freeSetupFileHash(setup_file_hash);
12429   }
12430
12431   free(filename);
12432 }
12433
12434 void LoadSetup(void)
12435 {
12436   LoadSetup_Default();
12437   LoadSetup_AutoSetup();
12438   LoadSetup_ServerSetup();
12439   LoadSetup_EditorCascade();
12440 }
12441
12442 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12443                                            char *mapping_line)
12444 {
12445   char mapping_guid[MAX_LINE_LEN];
12446   char *mapping_start, *mapping_end;
12447
12448   // get GUID from game controller mapping line: copy complete line
12449   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12450   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12451
12452   // get GUID from game controller mapping line: cut after GUID part
12453   mapping_start = strchr(mapping_guid, ',');
12454   if (mapping_start != NULL)
12455     *mapping_start = '\0';
12456
12457   // cut newline from game controller mapping line
12458   mapping_end = strchr(mapping_line, '\n');
12459   if (mapping_end != NULL)
12460     *mapping_end = '\0';
12461
12462   // add mapping entry to game controller mappings hash
12463   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12464 }
12465
12466 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12467                                                  char *filename)
12468 {
12469   FILE *file;
12470
12471   if (!(file = fopen(filename, MODE_READ)))
12472   {
12473     Warn("cannot read game controller mappings file '%s'", filename);
12474
12475     return;
12476   }
12477
12478   while (!feof(file))
12479   {
12480     char line[MAX_LINE_LEN];
12481
12482     if (!fgets(line, MAX_LINE_LEN, file))
12483       break;
12484
12485     addGameControllerMappingToHash(mappings_hash, line);
12486   }
12487
12488   fclose(file);
12489 }
12490
12491 void SaveSetup_Default(void)
12492 {
12493   char *filename = getSetupFilename();
12494   FILE *file;
12495   int i, pnr;
12496
12497   InitUserDataDirectory();
12498
12499   if (!(file = fopen(filename, MODE_WRITE)))
12500   {
12501     Warn("cannot write setup file '%s'", filename);
12502
12503     return;
12504   }
12505
12506   fprintFileHeader(file, SETUP_FILENAME);
12507
12508   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12509   {
12510     // just to make things nicer :)
12511     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12512         global_setup_tokens[i].value == &setup.sound                    ||
12513         global_setup_tokens[i].value == &setup.graphics_set             ||
12514         global_setup_tokens[i].value == &setup.volume_simple            ||
12515         global_setup_tokens[i].value == &setup.network_mode             ||
12516         global_setup_tokens[i].value == &setup.touch.control_type       ||
12517         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12518         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12519       fprintf(file, "\n");
12520
12521     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12522   }
12523
12524   for (i = 0; i < 2; i++)
12525   {
12526     int grid_xsize = setup.touch.grid_xsize[i];
12527     int grid_ysize = setup.touch.grid_ysize[i];
12528     int x, y;
12529
12530     fprintf(file, "\n");
12531
12532     for (y = 0; y < grid_ysize; y++)
12533     {
12534       char token_string[MAX_LINE_LEN];
12535       char value_string[MAX_LINE_LEN];
12536
12537       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12538
12539       for (x = 0; x < grid_xsize; x++)
12540       {
12541         char c = setup.touch.grid_button[i][x][y];
12542
12543         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12544       }
12545
12546       value_string[grid_xsize] = '\0';
12547
12548       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12549     }
12550   }
12551
12552   fprintf(file, "\n");
12553   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12554     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12555
12556   fprintf(file, "\n");
12557   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12558     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12559
12560   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12561   {
12562     char prefix[30];
12563
12564     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12565     fprintf(file, "\n");
12566
12567     setup_input = setup.input[pnr];
12568     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12569       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12570   }
12571
12572   fprintf(file, "\n");
12573   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12574     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12575
12576   // (internal setup values not saved to user setup file)
12577
12578   fprintf(file, "\n");
12579   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12580     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12581         setup.debug.xsn_mode != STATE_AUTO)
12582       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12583
12584   fprintf(file, "\n");
12585   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12586     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12587
12588   fclose(file);
12589
12590   SetFilePermissions(filename, PERMS_PRIVATE);
12591 }
12592
12593 void SaveSetup_AutoSetup(void)
12594 {
12595   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12596   FILE *file;
12597   int i;
12598
12599   InitUserDataDirectory();
12600
12601   if (!(file = fopen(filename, MODE_WRITE)))
12602   {
12603     Warn("cannot write auto setup file '%s'", filename);
12604
12605     free(filename);
12606
12607     return;
12608   }
12609
12610   fprintFileHeader(file, AUTOSETUP_FILENAME);
12611
12612   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12613     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12614
12615   fclose(file);
12616
12617   SetFilePermissions(filename, PERMS_PRIVATE);
12618
12619   free(filename);
12620 }
12621
12622 void SaveSetup_ServerSetup(void)
12623 {
12624   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12625   FILE *file;
12626   int i;
12627
12628   InitUserDataDirectory();
12629
12630   if (!(file = fopen(filename, MODE_WRITE)))
12631   {
12632     Warn("cannot write server setup file '%s'", filename);
12633
12634     free(filename);
12635
12636     return;
12637   }
12638
12639   fprintFileHeader(file, SERVERSETUP_FILENAME);
12640
12641   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12642   {
12643     // just to make things nicer :)
12644     if (server_setup_tokens[i].value == &setup.use_api_server)
12645       fprintf(file, "\n");
12646
12647     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12648   }
12649
12650   fclose(file);
12651
12652   SetFilePermissions(filename, PERMS_PRIVATE);
12653
12654   free(filename);
12655 }
12656
12657 void SaveSetup_EditorCascade(void)
12658 {
12659   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12660   FILE *file;
12661   int i;
12662
12663   InitUserDataDirectory();
12664
12665   if (!(file = fopen(filename, MODE_WRITE)))
12666   {
12667     Warn("cannot write editor cascade state file '%s'", filename);
12668
12669     free(filename);
12670
12671     return;
12672   }
12673
12674   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12675
12676   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12677     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12678
12679   fclose(file);
12680
12681   SetFilePermissions(filename, PERMS_PRIVATE);
12682
12683   free(filename);
12684 }
12685
12686 void SaveSetup(void)
12687 {
12688   SaveSetup_Default();
12689   SaveSetup_AutoSetup();
12690   SaveSetup_ServerSetup();
12691   SaveSetup_EditorCascade();
12692 }
12693
12694 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12695                                                   char *filename)
12696 {
12697   FILE *file;
12698
12699   if (!(file = fopen(filename, MODE_WRITE)))
12700   {
12701     Warn("cannot write game controller mappings file '%s'", filename);
12702
12703     return;
12704   }
12705
12706   BEGIN_HASH_ITERATION(mappings_hash, itr)
12707   {
12708     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12709   }
12710   END_HASH_ITERATION(mappings_hash, itr)
12711
12712   fclose(file);
12713 }
12714
12715 void SaveSetup_AddGameControllerMapping(char *mapping)
12716 {
12717   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12718   SetupFileHash *mappings_hash = newSetupFileHash();
12719
12720   InitUserDataDirectory();
12721
12722   // load existing personal game controller mappings
12723   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12724
12725   // add new mapping to personal game controller mappings
12726   addGameControllerMappingToHash(mappings_hash, mapping);
12727
12728   // save updated personal game controller mappings
12729   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12730
12731   freeSetupFileHash(mappings_hash);
12732   free(filename);
12733 }
12734
12735 void LoadCustomElementDescriptions(void)
12736 {
12737   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12738   SetupFileHash *setup_file_hash;
12739   int i;
12740
12741   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12742   {
12743     if (element_info[i].custom_description != NULL)
12744     {
12745       free(element_info[i].custom_description);
12746       element_info[i].custom_description = NULL;
12747     }
12748   }
12749
12750   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12751     return;
12752
12753   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12754   {
12755     char *token = getStringCat2(element_info[i].token_name, ".name");
12756     char *value = getHashEntry(setup_file_hash, token);
12757
12758     if (value != NULL)
12759       element_info[i].custom_description = getStringCopy(value);
12760
12761     free(token);
12762   }
12763
12764   freeSetupFileHash(setup_file_hash);
12765 }
12766
12767 static int getElementFromToken(char *token)
12768 {
12769   char *value = getHashEntry(element_token_hash, token);
12770
12771   if (value != NULL)
12772     return atoi(value);
12773
12774   Warn("unknown element token '%s'", token);
12775
12776   return EL_UNDEFINED;
12777 }
12778
12779 void FreeGlobalAnimEventInfo(void)
12780 {
12781   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12782
12783   if (gaei->event_list == NULL)
12784     return;
12785
12786   int i;
12787
12788   for (i = 0; i < gaei->num_event_lists; i++)
12789   {
12790     checked_free(gaei->event_list[i]->event_value);
12791     checked_free(gaei->event_list[i]);
12792   }
12793
12794   checked_free(gaei->event_list);
12795
12796   gaei->event_list = NULL;
12797   gaei->num_event_lists = 0;
12798 }
12799
12800 static int AddGlobalAnimEventList(void)
12801 {
12802   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12803   int list_pos = gaei->num_event_lists++;
12804
12805   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12806                                      sizeof(struct GlobalAnimEventListInfo *));
12807
12808   gaei->event_list[list_pos] =
12809     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12810
12811   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12812
12813   gaeli->event_value = NULL;
12814   gaeli->num_event_values = 0;
12815
12816   return list_pos;
12817 }
12818
12819 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12820 {
12821   // do not add empty global animation events
12822   if (event_value == ANIM_EVENT_NONE)
12823     return list_pos;
12824
12825   // if list position is undefined, create new list
12826   if (list_pos == ANIM_EVENT_UNDEFINED)
12827     list_pos = AddGlobalAnimEventList();
12828
12829   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12830   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12831   int value_pos = gaeli->num_event_values++;
12832
12833   gaeli->event_value = checked_realloc(gaeli->event_value,
12834                                        gaeli->num_event_values * sizeof(int *));
12835
12836   gaeli->event_value[value_pos] = event_value;
12837
12838   return list_pos;
12839 }
12840
12841 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12842 {
12843   if (list_pos == ANIM_EVENT_UNDEFINED)
12844     return ANIM_EVENT_NONE;
12845
12846   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12847   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12848
12849   return gaeli->event_value[value_pos];
12850 }
12851
12852 int GetGlobalAnimEventValueCount(int list_pos)
12853 {
12854   if (list_pos == ANIM_EVENT_UNDEFINED)
12855     return 0;
12856
12857   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12858   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12859
12860   return gaeli->num_event_values;
12861 }
12862
12863 // This function checks if a string <s> of the format "string1, string2, ..."
12864 // exactly contains a string <s_contained>.
12865
12866 static boolean string_has_parameter(char *s, char *s_contained)
12867 {
12868   char *substring;
12869
12870   if (s == NULL || s_contained == NULL)
12871     return FALSE;
12872
12873   if (strlen(s_contained) > strlen(s))
12874     return FALSE;
12875
12876   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12877   {
12878     char next_char = s[strlen(s_contained)];
12879
12880     // check if next character is delimiter or whitespace
12881     if (next_char == ',' || next_char == '\0' ||
12882         next_char == ' ' || next_char == '\t')
12883       return TRUE;
12884   }
12885
12886   // check if string contains another parameter string after a comma
12887   substring = strchr(s, ',');
12888   if (substring == NULL)        // string does not contain a comma
12889     return FALSE;
12890
12891   // advance string pointer to next character after the comma
12892   substring++;
12893
12894   // skip potential whitespaces after the comma
12895   while (*substring == ' ' || *substring == '\t')
12896     substring++;
12897
12898   return string_has_parameter(substring, s_contained);
12899 }
12900
12901 static int get_anim_parameter_value_ce(char *s)
12902 {
12903   char *s_ptr = s;
12904   char *pattern_1 = "ce_change:custom_";
12905   char *pattern_2 = ".page_";
12906   int pattern_1_len = strlen(pattern_1);
12907   char *matching_char = strstr(s_ptr, pattern_1);
12908   int result = ANIM_EVENT_NONE;
12909
12910   if (matching_char == NULL)
12911     return ANIM_EVENT_NONE;
12912
12913   result = ANIM_EVENT_CE_CHANGE;
12914
12915   s_ptr = matching_char + pattern_1_len;
12916
12917   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12918   if (*s_ptr >= '0' && *s_ptr <= '9')
12919   {
12920     int gic_ce_nr = (*s_ptr++ - '0');
12921
12922     if (*s_ptr >= '0' && *s_ptr <= '9')
12923     {
12924       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12925
12926       if (*s_ptr >= '0' && *s_ptr <= '9')
12927         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12928     }
12929
12930     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12931       return ANIM_EVENT_NONE;
12932
12933     // custom element stored as 0 to 255
12934     gic_ce_nr--;
12935
12936     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12937   }
12938   else
12939   {
12940     // invalid custom element number specified
12941
12942     return ANIM_EVENT_NONE;
12943   }
12944
12945   // check for change page number ("page_X" or "page_XX") (optional)
12946   if (strPrefix(s_ptr, pattern_2))
12947   {
12948     s_ptr += strlen(pattern_2);
12949
12950     if (*s_ptr >= '0' && *s_ptr <= '9')
12951     {
12952       int gic_page_nr = (*s_ptr++ - '0');
12953
12954       if (*s_ptr >= '0' && *s_ptr <= '9')
12955         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12956
12957       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12958         return ANIM_EVENT_NONE;
12959
12960       // change page stored as 1 to 32 (0 means "all change pages")
12961
12962       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12963     }
12964     else
12965     {
12966       // invalid animation part number specified
12967
12968       return ANIM_EVENT_NONE;
12969     }
12970   }
12971
12972   // discard result if next character is neither delimiter nor whitespace
12973   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12974         *s_ptr == ' ' || *s_ptr == '\t'))
12975     return ANIM_EVENT_NONE;
12976
12977   return result;
12978 }
12979
12980 static int get_anim_parameter_value(char *s)
12981 {
12982   int event_value[] =
12983   {
12984     ANIM_EVENT_CLICK,
12985     ANIM_EVENT_INIT,
12986     ANIM_EVENT_START,
12987     ANIM_EVENT_END,
12988     ANIM_EVENT_POST
12989   };
12990   char *pattern_1[] =
12991   {
12992     "click:anim_",
12993     "init:anim_",
12994     "start:anim_",
12995     "end:anim_",
12996     "post:anim_"
12997   };
12998   char *pattern_2 = ".part_";
12999   char *matching_char = NULL;
13000   char *s_ptr = s;
13001   int pattern_1_len = 0;
13002   int result = ANIM_EVENT_NONE;
13003   int i;
13004
13005   result = get_anim_parameter_value_ce(s);
13006
13007   if (result != ANIM_EVENT_NONE)
13008     return result;
13009
13010   for (i = 0; i < ARRAY_SIZE(event_value); i++)
13011   {
13012     matching_char = strstr(s_ptr, pattern_1[i]);
13013     pattern_1_len = strlen(pattern_1[i]);
13014     result = event_value[i];
13015
13016     if (matching_char != NULL)
13017       break;
13018   }
13019
13020   if (matching_char == NULL)
13021     return ANIM_EVENT_NONE;
13022
13023   s_ptr = matching_char + pattern_1_len;
13024
13025   // check for main animation number ("anim_X" or "anim_XX")
13026   if (*s_ptr >= '0' && *s_ptr <= '9')
13027   {
13028     int gic_anim_nr = (*s_ptr++ - '0');
13029
13030     if (*s_ptr >= '0' && *s_ptr <= '9')
13031       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
13032
13033     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
13034       return ANIM_EVENT_NONE;
13035
13036     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
13037   }
13038   else
13039   {
13040     // invalid main animation number specified
13041
13042     return ANIM_EVENT_NONE;
13043   }
13044
13045   // check for animation part number ("part_X" or "part_XX") (optional)
13046   if (strPrefix(s_ptr, pattern_2))
13047   {
13048     s_ptr += strlen(pattern_2);
13049
13050     if (*s_ptr >= '0' && *s_ptr <= '9')
13051     {
13052       int gic_part_nr = (*s_ptr++ - '0');
13053
13054       if (*s_ptr >= '0' && *s_ptr <= '9')
13055         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
13056
13057       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
13058         return ANIM_EVENT_NONE;
13059
13060       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
13061     }
13062     else
13063     {
13064       // invalid animation part number specified
13065
13066       return ANIM_EVENT_NONE;
13067     }
13068   }
13069
13070   // discard result if next character is neither delimiter nor whitespace
13071   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13072         *s_ptr == ' ' || *s_ptr == '\t'))
13073     return ANIM_EVENT_NONE;
13074
13075   return result;
13076 }
13077
13078 static int get_anim_parameter_values(char *s)
13079 {
13080   int list_pos = ANIM_EVENT_UNDEFINED;
13081   int event_value = ANIM_EVENT_DEFAULT;
13082
13083   if (string_has_parameter(s, "any"))
13084     event_value |= ANIM_EVENT_ANY;
13085
13086   if (string_has_parameter(s, "click:self") ||
13087       string_has_parameter(s, "click") ||
13088       string_has_parameter(s, "self"))
13089     event_value |= ANIM_EVENT_SELF;
13090
13091   if (string_has_parameter(s, "unclick:any"))
13092     event_value |= ANIM_EVENT_UNCLICK_ANY;
13093
13094   // if animation event found, add it to global animation event list
13095   if (event_value != ANIM_EVENT_NONE)
13096     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13097
13098   while (s != NULL)
13099   {
13100     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13101     event_value = get_anim_parameter_value(s);
13102
13103     // if animation event found, add it to global animation event list
13104     if (event_value != ANIM_EVENT_NONE)
13105       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13106
13107     // continue with next part of the string, starting with next comma
13108     s = strchr(s + 1, ',');
13109   }
13110
13111   return list_pos;
13112 }
13113
13114 static int get_anim_action_parameter_value(char *token)
13115 {
13116   // check most common default case first to massively speed things up
13117   if (strEqual(token, ARG_UNDEFINED))
13118     return ANIM_EVENT_ACTION_NONE;
13119
13120   int result = getImageIDFromToken(token);
13121
13122   if (result == -1)
13123   {
13124     char *gfx_token = getStringCat2("gfx.", token);
13125
13126     result = getImageIDFromToken(gfx_token);
13127
13128     checked_free(gfx_token);
13129   }
13130
13131   if (result == -1)
13132   {
13133     Key key = getKeyFromX11KeyName(token);
13134
13135     if (key != KSYM_UNDEFINED)
13136       result = -(int)key;
13137   }
13138
13139   if (result == -1)
13140   {
13141     if (isURL(token))
13142     {
13143       result = get_hash_from_string(token);     // unsigned int => int
13144       result = ABS(result);                     // may be negative now
13145       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13146
13147       setHashEntry(anim_url_hash, int2str(result, 0), token);
13148     }
13149   }
13150
13151   if (result == -1)
13152     result = ANIM_EVENT_ACTION_NONE;
13153
13154   return result;
13155 }
13156
13157 int get_parameter_value(char *value_raw, char *suffix, int type)
13158 {
13159   char *value = getStringToLower(value_raw);
13160   int result = 0;       // probably a save default value
13161
13162   if (strEqual(suffix, ".direction"))
13163   {
13164     result = (strEqual(value, "left")  ? MV_LEFT :
13165               strEqual(value, "right") ? MV_RIGHT :
13166               strEqual(value, "up")    ? MV_UP :
13167               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13168   }
13169   else if (strEqual(suffix, ".position"))
13170   {
13171     result = (strEqual(value, "left")   ? POS_LEFT :
13172               strEqual(value, "right")  ? POS_RIGHT :
13173               strEqual(value, "top")    ? POS_TOP :
13174               strEqual(value, "upper")  ? POS_UPPER :
13175               strEqual(value, "middle") ? POS_MIDDLE :
13176               strEqual(value, "lower")  ? POS_LOWER :
13177               strEqual(value, "bottom") ? POS_BOTTOM :
13178               strEqual(value, "any")    ? POS_ANY :
13179               strEqual(value, "ce")     ? POS_CE :
13180               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13181               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13182   }
13183   else if (strEqual(suffix, ".align"))
13184   {
13185     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13186               strEqual(value, "right")  ? ALIGN_RIGHT :
13187               strEqual(value, "center") ? ALIGN_CENTER :
13188               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13189   }
13190   else if (strEqual(suffix, ".valign"))
13191   {
13192     result = (strEqual(value, "top")    ? VALIGN_TOP :
13193               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13194               strEqual(value, "middle") ? VALIGN_MIDDLE :
13195               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13196   }
13197   else if (strEqual(suffix, ".anim_mode"))
13198   {
13199     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13200               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13201               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13202               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13203               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13204               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13205               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13206               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13207               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13208               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13209               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13210               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13211               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13212               string_has_parameter(value, "all")        ? ANIM_ALL :
13213               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13214               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13215               ANIM_DEFAULT);
13216
13217     if (string_has_parameter(value, "once"))
13218       result |= ANIM_ONCE;
13219
13220     if (string_has_parameter(value, "reverse"))
13221       result |= ANIM_REVERSE;
13222
13223     if (string_has_parameter(value, "opaque_player"))
13224       result |= ANIM_OPAQUE_PLAYER;
13225
13226     if (string_has_parameter(value, "static_panel"))
13227       result |= ANIM_STATIC_PANEL;
13228   }
13229   else if (strEqual(suffix, ".init_event") ||
13230            strEqual(suffix, ".anim_event"))
13231   {
13232     result = get_anim_parameter_values(value);
13233   }
13234   else if (strEqual(suffix, ".init_delay_action") ||
13235            strEqual(suffix, ".anim_delay_action") ||
13236            strEqual(suffix, ".post_delay_action") ||
13237            strEqual(suffix, ".init_event_action") ||
13238            strEqual(suffix, ".anim_event_action"))
13239   {
13240     result = get_anim_action_parameter_value(value_raw);
13241   }
13242   else if (strEqual(suffix, ".class"))
13243   {
13244     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13245               get_hash_from_string(value));
13246   }
13247   else if (strEqual(suffix, ".style"))
13248   {
13249     result = STYLE_DEFAULT;
13250
13251     if (string_has_parameter(value, "accurate_borders"))
13252       result |= STYLE_ACCURATE_BORDERS;
13253
13254     if (string_has_parameter(value, "inner_corners"))
13255       result |= STYLE_INNER_CORNERS;
13256
13257     if (string_has_parameter(value, "reverse"))
13258       result |= STYLE_REVERSE;
13259
13260     if (string_has_parameter(value, "leftmost_position"))
13261       result |= STYLE_LEFTMOST_POSITION;
13262
13263     if (string_has_parameter(value, "block_clicks"))
13264       result |= STYLE_BLOCK;
13265
13266     if (string_has_parameter(value, "passthrough_clicks"))
13267       result |= STYLE_PASSTHROUGH;
13268
13269     if (string_has_parameter(value, "multiple_actions"))
13270       result |= STYLE_MULTIPLE_ACTIONS;
13271
13272     if (string_has_parameter(value, "consume_ce_event"))
13273       result |= STYLE_CONSUME_CE_EVENT;
13274   }
13275   else if (strEqual(suffix, ".fade_mode"))
13276   {
13277     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13278               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13279               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13280               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13281               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13282               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13283               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13284               FADE_MODE_DEFAULT);
13285   }
13286   else if (strEqual(suffix, ".auto_delay_unit"))
13287   {
13288     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13289               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13290               AUTO_DELAY_UNIT_DEFAULT);
13291   }
13292   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13293   {
13294     result = gfx.get_font_from_token_function(value);
13295   }
13296   else          // generic parameter of type integer or boolean
13297   {
13298     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13299               type == TYPE_INTEGER ? get_integer_from_string(value) :
13300               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13301               ARG_UNDEFINED_VALUE);
13302   }
13303
13304   free(value);
13305
13306   return result;
13307 }
13308
13309 static int get_token_parameter_value(char *token, char *value_raw)
13310 {
13311   char *suffix;
13312
13313   if (token == NULL || value_raw == NULL)
13314     return ARG_UNDEFINED_VALUE;
13315
13316   suffix = strrchr(token, '.');
13317   if (suffix == NULL)
13318     suffix = token;
13319
13320   if (strEqual(suffix, ".element"))
13321     return getElementFromToken(value_raw);
13322
13323   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13324   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13325 }
13326
13327 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13328                                      boolean ignore_defaults)
13329 {
13330   int i;
13331
13332   for (i = 0; image_config_vars[i].token != NULL; i++)
13333   {
13334     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13335
13336     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13337     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13338       continue;
13339
13340     if (value != NULL)
13341       *image_config_vars[i].value =
13342         get_token_parameter_value(image_config_vars[i].token, value);
13343   }
13344 }
13345
13346 void InitMenuDesignSettings_Static(void)
13347 {
13348   // always start with reliable default values from static default config
13349   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13350 }
13351
13352 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13353 {
13354   int i;
13355
13356   // the following initializes hierarchical values from static configuration
13357
13358   // special case: initialize "ARG_DEFAULT" values in static default config
13359   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13360   titlescreen_initial_first_default.fade_mode  =
13361     title_initial_first_default.fade_mode;
13362   titlescreen_initial_first_default.fade_delay =
13363     title_initial_first_default.fade_delay;
13364   titlescreen_initial_first_default.post_delay =
13365     title_initial_first_default.post_delay;
13366   titlescreen_initial_first_default.auto_delay =
13367     title_initial_first_default.auto_delay;
13368   titlescreen_initial_first_default.auto_delay_unit =
13369     title_initial_first_default.auto_delay_unit;
13370   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13371   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13372   titlescreen_first_default.post_delay = title_first_default.post_delay;
13373   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13374   titlescreen_first_default.auto_delay_unit =
13375     title_first_default.auto_delay_unit;
13376   titlemessage_initial_first_default.fade_mode  =
13377     title_initial_first_default.fade_mode;
13378   titlemessage_initial_first_default.fade_delay =
13379     title_initial_first_default.fade_delay;
13380   titlemessage_initial_first_default.post_delay =
13381     title_initial_first_default.post_delay;
13382   titlemessage_initial_first_default.auto_delay =
13383     title_initial_first_default.auto_delay;
13384   titlemessage_initial_first_default.auto_delay_unit =
13385     title_initial_first_default.auto_delay_unit;
13386   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13387   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13388   titlemessage_first_default.post_delay = title_first_default.post_delay;
13389   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13390   titlemessage_first_default.auto_delay_unit =
13391     title_first_default.auto_delay_unit;
13392
13393   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13394   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13395   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13396   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13397   titlescreen_initial_default.auto_delay_unit =
13398     title_initial_default.auto_delay_unit;
13399   titlescreen_default.fade_mode  = title_default.fade_mode;
13400   titlescreen_default.fade_delay = title_default.fade_delay;
13401   titlescreen_default.post_delay = title_default.post_delay;
13402   titlescreen_default.auto_delay = title_default.auto_delay;
13403   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13404   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13405   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13406   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13407   titlemessage_initial_default.auto_delay_unit =
13408     title_initial_default.auto_delay_unit;
13409   titlemessage_default.fade_mode  = title_default.fade_mode;
13410   titlemessage_default.fade_delay = title_default.fade_delay;
13411   titlemessage_default.post_delay = title_default.post_delay;
13412   titlemessage_default.auto_delay = title_default.auto_delay;
13413   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13414
13415   // special case: initialize "ARG_DEFAULT" values in static default config
13416   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13417   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13418   {
13419     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13420     titlescreen_first[i] = titlescreen_first_default;
13421     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13422     titlemessage_first[i] = titlemessage_first_default;
13423
13424     titlescreen_initial[i] = titlescreen_initial_default;
13425     titlescreen[i] = titlescreen_default;
13426     titlemessage_initial[i] = titlemessage_initial_default;
13427     titlemessage[i] = titlemessage_default;
13428   }
13429
13430   // special case: initialize "ARG_DEFAULT" values in static default config
13431   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13432   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13433   {
13434     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13435       continue;
13436
13437     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13438     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13439     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13440   }
13441
13442   // special case: initialize "ARG_DEFAULT" values in static default config
13443   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13444   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13445   {
13446     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13447     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13448     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13449
13450     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13451       continue;
13452
13453     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13454   }
13455 }
13456
13457 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13458 {
13459   static struct
13460   {
13461     struct XY *dst, *src;
13462   }
13463   game_buttons_xy[] =
13464   {
13465     { &game.button.save,        &game.button.stop       },
13466     { &game.button.pause2,      &game.button.pause      },
13467     { &game.button.load,        &game.button.play       },
13468     { &game.button.undo,        &game.button.stop       },
13469     { &game.button.redo,        &game.button.play       },
13470
13471     { NULL,                     NULL                    }
13472   };
13473   int i, j;
13474
13475   // special case: initialize later added SETUP list size from LEVELS value
13476   if (menu.list_size[GAME_MODE_SETUP] == -1)
13477     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13478
13479   // set default position for snapshot buttons to stop/pause/play buttons
13480   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13481     if ((*game_buttons_xy[i].dst).x == -1 &&
13482         (*game_buttons_xy[i].dst).y == -1)
13483       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13484
13485   // --------------------------------------------------------------------------
13486   // dynamic viewports (including playfield margins, borders and alignments)
13487   // --------------------------------------------------------------------------
13488
13489   // dynamic viewports currently only supported for landscape mode
13490   int display_width  = MAX(video.display_width, video.display_height);
13491   int display_height = MIN(video.display_width, video.display_height);
13492
13493   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13494   {
13495     struct RectWithBorder *vp_window    = &viewport.window[i];
13496     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13497     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13498     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13499     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13500     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13501     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13502     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13503
13504     // adjust window size if min/max width/height is specified
13505
13506     if (vp_window->min_width != -1)
13507     {
13508       int window_width = display_width;
13509
13510       // when using static window height, use aspect ratio of display
13511       if (vp_window->min_height == -1)
13512         window_width = vp_window->height * display_width / display_height;
13513
13514       vp_window->width = MAX(vp_window->min_width, window_width);
13515     }
13516
13517     if (vp_window->min_height != -1)
13518     {
13519       int window_height = display_height;
13520
13521       // when using static window width, use aspect ratio of display
13522       if (vp_window->min_width == -1)
13523         window_height = vp_window->width * display_height / display_width;
13524
13525       vp_window->height = MAX(vp_window->min_height, window_height);
13526     }
13527
13528     if (vp_window->max_width != -1)
13529       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13530
13531     if (vp_window->max_height != -1)
13532       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13533
13534     int playfield_width  = vp_window->width;
13535     int playfield_height = vp_window->height;
13536
13537     // adjust playfield size and position according to specified margins
13538
13539     playfield_width  -= vp_playfield->margin_left;
13540     playfield_width  -= vp_playfield->margin_right;
13541
13542     playfield_height -= vp_playfield->margin_top;
13543     playfield_height -= vp_playfield->margin_bottom;
13544
13545     // adjust playfield size if min/max width/height is specified
13546
13547     if (vp_playfield->min_width != -1)
13548       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13549
13550     if (vp_playfield->min_height != -1)
13551       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13552
13553     if (vp_playfield->max_width != -1)
13554       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13555
13556     if (vp_playfield->max_height != -1)
13557       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13558
13559     // adjust playfield position according to specified alignment
13560
13561     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13562       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13563     else if (vp_playfield->align == ALIGN_CENTER)
13564       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13565     else if (vp_playfield->align == ALIGN_RIGHT)
13566       vp_playfield->x += playfield_width - vp_playfield->width;
13567
13568     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13569       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13570     else if (vp_playfield->valign == VALIGN_MIDDLE)
13571       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13572     else if (vp_playfield->valign == VALIGN_BOTTOM)
13573       vp_playfield->y += playfield_height - vp_playfield->height;
13574
13575     vp_playfield->x += vp_playfield->margin_left;
13576     vp_playfield->y += vp_playfield->margin_top;
13577
13578     // adjust individual playfield borders if only default border is specified
13579
13580     if (vp_playfield->border_left == -1)
13581       vp_playfield->border_left = vp_playfield->border_size;
13582     if (vp_playfield->border_right == -1)
13583       vp_playfield->border_right = vp_playfield->border_size;
13584     if (vp_playfield->border_top == -1)
13585       vp_playfield->border_top = vp_playfield->border_size;
13586     if (vp_playfield->border_bottom == -1)
13587       vp_playfield->border_bottom = vp_playfield->border_size;
13588
13589     // set dynamic playfield borders if borders are specified as undefined
13590     // (but only if window size was dynamic and playfield size was static)
13591
13592     if (dynamic_window_width && !dynamic_playfield_width)
13593     {
13594       if (vp_playfield->border_left == -1)
13595       {
13596         vp_playfield->border_left = (vp_playfield->x -
13597                                      vp_playfield->margin_left);
13598         vp_playfield->x     -= vp_playfield->border_left;
13599         vp_playfield->width += vp_playfield->border_left;
13600       }
13601
13602       if (vp_playfield->border_right == -1)
13603       {
13604         vp_playfield->border_right = (vp_window->width -
13605                                       vp_playfield->x -
13606                                       vp_playfield->width -
13607                                       vp_playfield->margin_right);
13608         vp_playfield->width += vp_playfield->border_right;
13609       }
13610     }
13611
13612     if (dynamic_window_height && !dynamic_playfield_height)
13613     {
13614       if (vp_playfield->border_top == -1)
13615       {
13616         vp_playfield->border_top = (vp_playfield->y -
13617                                     vp_playfield->margin_top);
13618         vp_playfield->y      -= vp_playfield->border_top;
13619         vp_playfield->height += vp_playfield->border_top;
13620       }
13621
13622       if (vp_playfield->border_bottom == -1)
13623       {
13624         vp_playfield->border_bottom = (vp_window->height -
13625                                        vp_playfield->y -
13626                                        vp_playfield->height -
13627                                        vp_playfield->margin_bottom);
13628         vp_playfield->height += vp_playfield->border_bottom;
13629       }
13630     }
13631
13632     // adjust playfield size to be a multiple of a defined alignment tile size
13633
13634     int align_size = vp_playfield->align_size;
13635     int playfield_xtiles = vp_playfield->width  / align_size;
13636     int playfield_ytiles = vp_playfield->height / align_size;
13637     int playfield_width_corrected  = playfield_xtiles * align_size;
13638     int playfield_height_corrected = playfield_ytiles * align_size;
13639     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13640                                  i == GFX_SPECIAL_ARG_EDITOR);
13641
13642     if (is_playfield_mode &&
13643         dynamic_playfield_width &&
13644         vp_playfield->width != playfield_width_corrected)
13645     {
13646       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13647
13648       vp_playfield->width = playfield_width_corrected;
13649
13650       if (vp_playfield->align == ALIGN_LEFT)
13651       {
13652         vp_playfield->border_left += playfield_xdiff;
13653       }
13654       else if (vp_playfield->align == ALIGN_RIGHT)
13655       {
13656         vp_playfield->border_right += playfield_xdiff;
13657       }
13658       else if (vp_playfield->align == ALIGN_CENTER)
13659       {
13660         int border_left_diff  = playfield_xdiff / 2;
13661         int border_right_diff = playfield_xdiff - border_left_diff;
13662
13663         vp_playfield->border_left  += border_left_diff;
13664         vp_playfield->border_right += border_right_diff;
13665       }
13666     }
13667
13668     if (is_playfield_mode &&
13669         dynamic_playfield_height &&
13670         vp_playfield->height != playfield_height_corrected)
13671     {
13672       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13673
13674       vp_playfield->height = playfield_height_corrected;
13675
13676       if (vp_playfield->valign == VALIGN_TOP)
13677       {
13678         vp_playfield->border_top += playfield_ydiff;
13679       }
13680       else if (vp_playfield->align == VALIGN_BOTTOM)
13681       {
13682         vp_playfield->border_right += playfield_ydiff;
13683       }
13684       else if (vp_playfield->align == VALIGN_MIDDLE)
13685       {
13686         int border_top_diff    = playfield_ydiff / 2;
13687         int border_bottom_diff = playfield_ydiff - border_top_diff;
13688
13689         vp_playfield->border_top    += border_top_diff;
13690         vp_playfield->border_bottom += border_bottom_diff;
13691       }
13692     }
13693
13694     // adjust door positions according to specified alignment
13695
13696     for (j = 0; j < 2; j++)
13697     {
13698       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13699
13700       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13701         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13702       else if (vp_door->align == ALIGN_CENTER)
13703         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13704       else if (vp_door->align == ALIGN_RIGHT)
13705         vp_door->x += vp_window->width - vp_door->width;
13706
13707       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13708         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13709       else if (vp_door->valign == VALIGN_MIDDLE)
13710         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13711       else if (vp_door->valign == VALIGN_BOTTOM)
13712         vp_door->y += vp_window->height - vp_door->height;
13713     }
13714   }
13715 }
13716
13717 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13718 {
13719   static struct
13720   {
13721     struct XYTileSize *dst, *src;
13722     int graphic;
13723   }
13724   editor_buttons_xy[] =
13725   {
13726     {
13727       &editor.button.element_left,      &editor.palette.element_left,
13728       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13729     },
13730     {
13731       &editor.button.element_middle,    &editor.palette.element_middle,
13732       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13733     },
13734     {
13735       &editor.button.element_right,     &editor.palette.element_right,
13736       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13737     },
13738
13739     { NULL,                     NULL                    }
13740   };
13741   int i;
13742
13743   // set default position for element buttons to element graphics
13744   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13745   {
13746     if ((*editor_buttons_xy[i].dst).x == -1 &&
13747         (*editor_buttons_xy[i].dst).y == -1)
13748     {
13749       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13750
13751       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13752
13753       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13754     }
13755   }
13756
13757   // adjust editor palette rows and columns if specified to be dynamic
13758
13759   if (editor.palette.cols == -1)
13760   {
13761     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13762     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13763     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13764
13765     editor.palette.cols = (vp_width - sc_width) / bt_width;
13766
13767     if (editor.palette.x == -1)
13768     {
13769       int palette_width = editor.palette.cols * bt_width + sc_width;
13770
13771       editor.palette.x = (vp_width - palette_width) / 2;
13772     }
13773   }
13774
13775   if (editor.palette.rows == -1)
13776   {
13777     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13778     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13779     int tx_height = getFontHeight(FONT_TEXT_2);
13780
13781     editor.palette.rows = (vp_height - tx_height) / bt_height;
13782
13783     if (editor.palette.y == -1)
13784     {
13785       int palette_height = editor.palette.rows * bt_height + tx_height;
13786
13787       editor.palette.y = (vp_height - palette_height) / 2;
13788     }
13789   }
13790 }
13791
13792 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13793                                                       boolean initialize)
13794 {
13795   // special case: check if network and preview player positions are redefined,
13796   // to compare this later against the main menu level preview being redefined
13797   struct TokenIntPtrInfo menu_config_players[] =
13798   {
13799     { "main.network_players.x", &menu.main.network_players.redefined    },
13800     { "main.network_players.y", &menu.main.network_players.redefined    },
13801     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13802     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13803     { "preview.x",              &preview.redefined                      },
13804     { "preview.y",              &preview.redefined                      }
13805   };
13806   int i;
13807
13808   if (initialize)
13809   {
13810     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13811       *menu_config_players[i].value = FALSE;
13812   }
13813   else
13814   {
13815     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13816       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13817         *menu_config_players[i].value = TRUE;
13818   }
13819 }
13820
13821 static void InitMenuDesignSettings_PreviewPlayers(void)
13822 {
13823   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13824 }
13825
13826 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13827 {
13828   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13829 }
13830
13831 static void LoadMenuDesignSettingsFromFilename(char *filename)
13832 {
13833   static struct TitleFadingInfo tfi;
13834   static struct TitleMessageInfo tmi;
13835   static struct TokenInfo title_tokens[] =
13836   {
13837     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13838     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13839     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13840     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13841     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13842
13843     { -1,               NULL,                   NULL                    }
13844   };
13845   static struct TokenInfo titlemessage_tokens[] =
13846   {
13847     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13848     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13849     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13850     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13851     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13852     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13853     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13854     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13855     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13856     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13857     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13858     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13859     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13860     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13861     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13862     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13863     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13864     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13865
13866     { -1,               NULL,                   NULL                    }
13867   };
13868   static struct
13869   {
13870     struct TitleFadingInfo *info;
13871     char *text;
13872   }
13873   title_info[] =
13874   {
13875     // initialize first titles from "enter screen" definitions, if defined
13876     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13877     { &title_first_default,             "menu.enter_screen.TITLE"       },
13878
13879     // initialize title screens from "next screen" definitions, if defined
13880     { &title_initial_default,           "menu.next_screen.TITLE"        },
13881     { &title_default,                   "menu.next_screen.TITLE"        },
13882
13883     { NULL,                             NULL                            }
13884   };
13885   static struct
13886   {
13887     struct TitleMessageInfo *array;
13888     char *text;
13889   }
13890   titlemessage_arrays[] =
13891   {
13892     // initialize first titles from "enter screen" definitions, if defined
13893     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13894     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13895     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13896     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13897
13898     // initialize titles from "next screen" definitions, if defined
13899     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13900     { titlescreen,                      "menu.next_screen.TITLE"        },
13901     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13902     { titlemessage,                     "menu.next_screen.TITLE"        },
13903
13904     // overwrite titles with title definitions, if defined
13905     { titlescreen_initial_first,        "[title_initial]"               },
13906     { titlescreen_first,                "[title]"                       },
13907     { titlemessage_initial_first,       "[title_initial]"               },
13908     { titlemessage_first,               "[title]"                       },
13909
13910     { titlescreen_initial,              "[title_initial]"               },
13911     { titlescreen,                      "[title]"                       },
13912     { titlemessage_initial,             "[title_initial]"               },
13913     { titlemessage,                     "[title]"                       },
13914
13915     // overwrite titles with title screen/message definitions, if defined
13916     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13917     { titlescreen_first,                "[titlescreen]"                 },
13918     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13919     { titlemessage_first,               "[titlemessage]"                },
13920
13921     { titlescreen_initial,              "[titlescreen_initial]"         },
13922     { titlescreen,                      "[titlescreen]"                 },
13923     { titlemessage_initial,             "[titlemessage_initial]"        },
13924     { titlemessage,                     "[titlemessage]"                },
13925
13926     { NULL,                             NULL                            }
13927   };
13928   SetupFileHash *setup_file_hash;
13929   int i, j, k;
13930
13931   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13932     return;
13933
13934   // the following initializes hierarchical values from dynamic configuration
13935
13936   // special case: initialize with default values that may be overwritten
13937   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13938   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13939   {
13940     struct TokenIntPtrInfo menu_config[] =
13941     {
13942       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13943       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13944       { "menu.list_size",       &menu.list_size[i]      }
13945     };
13946
13947     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13948     {
13949       char *token = menu_config[j].token;
13950       char *value = getHashEntry(setup_file_hash, token);
13951
13952       if (value != NULL)
13953         *menu_config[j].value = get_integer_from_string(value);
13954     }
13955   }
13956
13957   // special case: initialize with default values that may be overwritten
13958   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13959   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13960   {
13961     struct TokenIntPtrInfo menu_config[] =
13962     {
13963       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13964       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13965       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13966       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13967       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13968     };
13969
13970     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13971     {
13972       char *token = menu_config[j].token;
13973       char *value = getHashEntry(setup_file_hash, token);
13974
13975       if (value != NULL)
13976         *menu_config[j].value = get_integer_from_string(value);
13977     }
13978   }
13979
13980   // special case: initialize with default values that may be overwritten
13981   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13982   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13983   {
13984     struct TokenIntPtrInfo menu_config[] =
13985     {
13986       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13987       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13988     };
13989
13990     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13991     {
13992       char *token = menu_config[j].token;
13993       char *value = getHashEntry(setup_file_hash, token);
13994
13995       if (value != NULL)
13996         *menu_config[j].value = get_integer_from_string(value);
13997     }
13998   }
13999
14000   // special case: initialize with default values that may be overwritten
14001   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
14002   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
14003   {
14004     struct TokenIntPtrInfo menu_config[] =
14005     {
14006       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
14007       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
14008       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
14009       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
14010       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
14011       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
14012       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
14013       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
14014       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
14015       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
14016     };
14017
14018     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14019     {
14020       char *token = menu_config[j].token;
14021       char *value = getHashEntry(setup_file_hash, token);
14022
14023       if (value != NULL)
14024         *menu_config[j].value = get_integer_from_string(value);
14025     }
14026   }
14027
14028   // special case: initialize with default values that may be overwritten
14029   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
14030   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14031   {
14032     struct TokenIntPtrInfo menu_config[] =
14033     {
14034       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
14035       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
14036       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
14037       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
14038       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
14039       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
14040       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
14041       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
14042       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
14043     };
14044
14045     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14046     {
14047       char *token = menu_config[j].token;
14048       char *value = getHashEntry(setup_file_hash, token);
14049
14050       if (value != NULL)
14051         *menu_config[j].value = get_token_parameter_value(token, value);
14052     }
14053   }
14054
14055   // special case: initialize with default values that may be overwritten
14056   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
14057   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14058   {
14059     struct
14060     {
14061       char *token_prefix;
14062       struct RectWithBorder *struct_ptr;
14063     }
14064     vp_struct[] =
14065     {
14066       { "viewport.window",      &viewport.window[i]     },
14067       { "viewport.playfield",   &viewport.playfield[i]  },
14068       { "viewport.door_1",      &viewport.door_1[i]     },
14069       { "viewport.door_2",      &viewport.door_2[i]     }
14070     };
14071
14072     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14073     {
14074       struct TokenIntPtrInfo vp_config[] =
14075       {
14076         { ".x",                 &vp_struct[j].struct_ptr->x             },
14077         { ".y",                 &vp_struct[j].struct_ptr->y             },
14078         { ".width",             &vp_struct[j].struct_ptr->width         },
14079         { ".height",            &vp_struct[j].struct_ptr->height        },
14080         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
14081         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
14082         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
14083         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
14084         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
14085         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
14086         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
14087         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
14088         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
14089         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
14090         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
14091         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
14092         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
14093         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
14094         { ".align",             &vp_struct[j].struct_ptr->align         },
14095         { ".valign",            &vp_struct[j].struct_ptr->valign        }
14096       };
14097
14098       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14099       {
14100         char *token = getStringCat2(vp_struct[j].token_prefix,
14101                                     vp_config[k].token);
14102         char *value = getHashEntry(setup_file_hash, token);
14103
14104         if (value != NULL)
14105           *vp_config[k].value = get_token_parameter_value(token, value);
14106
14107         free(token);
14108       }
14109     }
14110   }
14111
14112   // special case: initialize with default values that may be overwritten
14113   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14114   for (i = 0; title_info[i].info != NULL; i++)
14115   {
14116     struct TitleFadingInfo *info = title_info[i].info;
14117     char *base_token = title_info[i].text;
14118
14119     for (j = 0; title_tokens[j].type != -1; j++)
14120     {
14121       char *token = getStringCat2(base_token, title_tokens[j].text);
14122       char *value = getHashEntry(setup_file_hash, token);
14123
14124       if (value != NULL)
14125       {
14126         int parameter_value = get_token_parameter_value(token, value);
14127
14128         tfi = *info;
14129
14130         *(int *)title_tokens[j].value = (int)parameter_value;
14131
14132         *info = tfi;
14133       }
14134
14135       free(token);
14136     }
14137   }
14138
14139   // special case: initialize with default values that may be overwritten
14140   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14141   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14142   {
14143     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14144     char *base_token = titlemessage_arrays[i].text;
14145
14146     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14147     {
14148       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14149       char *value = getHashEntry(setup_file_hash, token);
14150
14151       if (value != NULL)
14152       {
14153         int parameter_value = get_token_parameter_value(token, value);
14154
14155         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14156         {
14157           tmi = array[k];
14158
14159           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14160             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14161           else
14162             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14163
14164           array[k] = tmi;
14165         }
14166       }
14167
14168       free(token);
14169     }
14170   }
14171
14172   // read (and overwrite with) values that may be specified in config file
14173   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14174
14175   // special case: check if network and preview player positions are redefined
14176   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14177
14178   freeSetupFileHash(setup_file_hash);
14179 }
14180
14181 void LoadMenuDesignSettings(void)
14182 {
14183   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14184
14185   InitMenuDesignSettings_Static();
14186   InitMenuDesignSettings_SpecialPreProcessing();
14187   InitMenuDesignSettings_PreviewPlayers();
14188
14189   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14190   {
14191     // first look for special settings configured in level series config
14192     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14193
14194     if (fileExists(filename_base))
14195       LoadMenuDesignSettingsFromFilename(filename_base);
14196   }
14197
14198   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14199
14200   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14201     LoadMenuDesignSettingsFromFilename(filename_local);
14202
14203   InitMenuDesignSettings_SpecialPostProcessing();
14204 }
14205
14206 void LoadMenuDesignSettings_AfterGraphics(void)
14207 {
14208   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14209 }
14210
14211 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14212                                 boolean ignore_defaults)
14213 {
14214   int i;
14215
14216   for (i = 0; sound_config_vars[i].token != NULL; i++)
14217   {
14218     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14219
14220     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14221     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14222       continue;
14223
14224     if (value != NULL)
14225       *sound_config_vars[i].value =
14226         get_token_parameter_value(sound_config_vars[i].token, value);
14227   }
14228 }
14229
14230 void InitSoundSettings_Static(void)
14231 {
14232   // always start with reliable default values from static default config
14233   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14234 }
14235
14236 static void LoadSoundSettingsFromFilename(char *filename)
14237 {
14238   SetupFileHash *setup_file_hash;
14239
14240   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14241     return;
14242
14243   // read (and overwrite with) values that may be specified in config file
14244   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14245
14246   freeSetupFileHash(setup_file_hash);
14247 }
14248
14249 void LoadSoundSettings(void)
14250 {
14251   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14252
14253   InitSoundSettings_Static();
14254
14255   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14256   {
14257     // first look for special settings configured in level series config
14258     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14259
14260     if (fileExists(filename_base))
14261       LoadSoundSettingsFromFilename(filename_base);
14262   }
14263
14264   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14265
14266   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14267     LoadSoundSettingsFromFilename(filename_local);
14268 }
14269
14270 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14271 {
14272   char *filename = getEditorSetupFilename();
14273   SetupFileList *setup_file_list, *list;
14274   SetupFileHash *element_hash;
14275   int num_unknown_tokens = 0;
14276   int i;
14277
14278   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14279     return;
14280
14281   element_hash = newSetupFileHash();
14282
14283   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14284     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14285
14286   // determined size may be larger than needed (due to unknown elements)
14287   *num_elements = 0;
14288   for (list = setup_file_list; list != NULL; list = list->next)
14289     (*num_elements)++;
14290
14291   // add space for up to 3 more elements for padding that may be needed
14292   *num_elements += 3;
14293
14294   // free memory for old list of elements, if needed
14295   checked_free(*elements);
14296
14297   // allocate memory for new list of elements
14298   *elements = checked_malloc(*num_elements * sizeof(int));
14299
14300   *num_elements = 0;
14301   for (list = setup_file_list; list != NULL; list = list->next)
14302   {
14303     char *value = getHashEntry(element_hash, list->token);
14304
14305     if (value == NULL)          // try to find obsolete token mapping
14306     {
14307       char *mapped_token = get_mapped_token(list->token);
14308
14309       if (mapped_token != NULL)
14310       {
14311         value = getHashEntry(element_hash, mapped_token);
14312
14313         free(mapped_token);
14314       }
14315     }
14316
14317     if (value != NULL)
14318     {
14319       (*elements)[(*num_elements)++] = atoi(value);
14320     }
14321     else
14322     {
14323       if (num_unknown_tokens == 0)
14324       {
14325         Warn("---");
14326         Warn("unknown token(s) found in config file:");
14327         Warn("- config file: '%s'", filename);
14328
14329         num_unknown_tokens++;
14330       }
14331
14332       Warn("- token: '%s'", list->token);
14333     }
14334   }
14335
14336   if (num_unknown_tokens > 0)
14337     Warn("---");
14338
14339   while (*num_elements % 4)     // pad with empty elements, if needed
14340     (*elements)[(*num_elements)++] = EL_EMPTY;
14341
14342   freeSetupFileList(setup_file_list);
14343   freeSetupFileHash(element_hash);
14344
14345 #if 0
14346   for (i = 0; i < *num_elements; i++)
14347     Debug("editor", "element '%s' [%d]\n",
14348           element_info[(*elements)[i]].token_name, (*elements)[i]);
14349 #endif
14350 }
14351
14352 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14353                                                      boolean is_sound)
14354 {
14355   SetupFileHash *setup_file_hash = NULL;
14356   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14357   char *filename_music, *filename_prefix, *filename_info;
14358   struct
14359   {
14360     char *token;
14361     char **value_ptr;
14362   }
14363   token_to_value_ptr[] =
14364   {
14365     { "title_header",   &tmp_music_file_info.title_header       },
14366     { "artist_header",  &tmp_music_file_info.artist_header      },
14367     { "album_header",   &tmp_music_file_info.album_header       },
14368     { "year_header",    &tmp_music_file_info.year_header        },
14369     { "played_header",  &tmp_music_file_info.played_header      },
14370
14371     { "title",          &tmp_music_file_info.title              },
14372     { "artist",         &tmp_music_file_info.artist             },
14373     { "album",          &tmp_music_file_info.album              },
14374     { "year",           &tmp_music_file_info.year               },
14375     { "played",         &tmp_music_file_info.played             },
14376
14377     { NULL,             NULL                                    },
14378   };
14379   int i;
14380
14381   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14382                     getCustomMusicFilename(basename));
14383
14384   if (filename_music == NULL)
14385     return NULL;
14386
14387   // ---------- try to replace file extension ----------
14388
14389   filename_prefix = getStringCopy(filename_music);
14390   if (strrchr(filename_prefix, '.') != NULL)
14391     *strrchr(filename_prefix, '.') = '\0';
14392   filename_info = getStringCat2(filename_prefix, ".txt");
14393
14394   if (fileExists(filename_info))
14395     setup_file_hash = loadSetupFileHash(filename_info);
14396
14397   free(filename_prefix);
14398   free(filename_info);
14399
14400   if (setup_file_hash == NULL)
14401   {
14402     // ---------- try to add file extension ----------
14403
14404     filename_prefix = getStringCopy(filename_music);
14405     filename_info = getStringCat2(filename_prefix, ".txt");
14406
14407     if (fileExists(filename_info))
14408       setup_file_hash = loadSetupFileHash(filename_info);
14409
14410     free(filename_prefix);
14411     free(filename_info);
14412   }
14413
14414   if (setup_file_hash == NULL)
14415     return NULL;
14416
14417   // ---------- music file info found ----------
14418
14419   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14420
14421   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14422   {
14423     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14424
14425     *token_to_value_ptr[i].value_ptr =
14426       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14427   }
14428
14429   tmp_music_file_info.basename = getStringCopy(basename);
14430   tmp_music_file_info.music = music;
14431   tmp_music_file_info.is_sound = is_sound;
14432
14433   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14434   *new_music_file_info = tmp_music_file_info;
14435
14436   return new_music_file_info;
14437 }
14438
14439 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14440 {
14441   return get_music_file_info_ext(basename, music, FALSE);
14442 }
14443
14444 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14445 {
14446   return get_music_file_info_ext(basename, sound, TRUE);
14447 }
14448
14449 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14450                                      char *basename, boolean is_sound)
14451 {
14452   for (; list != NULL; list = list->next)
14453     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14454       return TRUE;
14455
14456   return FALSE;
14457 }
14458
14459 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14460 {
14461   return music_info_listed_ext(list, basename, FALSE);
14462 }
14463
14464 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14465 {
14466   return music_info_listed_ext(list, basename, TRUE);
14467 }
14468
14469 void LoadMusicInfo(void)
14470 {
14471   int num_music_noconf = getMusicListSize_NoConf();
14472   int num_music = getMusicListSize();
14473   int num_sounds = getSoundListSize();
14474   struct FileInfo *music, *sound;
14475   struct MusicFileInfo *next, **new;
14476
14477   int i;
14478
14479   while (music_file_info != NULL)
14480   {
14481     next = music_file_info->next;
14482
14483     checked_free(music_file_info->basename);
14484
14485     checked_free(music_file_info->title_header);
14486     checked_free(music_file_info->artist_header);
14487     checked_free(music_file_info->album_header);
14488     checked_free(music_file_info->year_header);
14489     checked_free(music_file_info->played_header);
14490
14491     checked_free(music_file_info->title);
14492     checked_free(music_file_info->artist);
14493     checked_free(music_file_info->album);
14494     checked_free(music_file_info->year);
14495     checked_free(music_file_info->played);
14496
14497     free(music_file_info);
14498
14499     music_file_info = next;
14500   }
14501
14502   new = &music_file_info;
14503
14504   // get (configured or unconfigured) music file info for all levels
14505   for (i = leveldir_current->first_level;
14506        i <= leveldir_current->last_level; i++)
14507   {
14508     int music_nr;
14509
14510     if (levelset.music[i] != MUS_UNDEFINED)
14511     {
14512       // get music file info for configured level music
14513       music_nr = levelset.music[i];
14514     }
14515     else if (num_music_noconf > 0)
14516     {
14517       // get music file info for unconfigured level music
14518       int level_pos = i - leveldir_current->first_level;
14519
14520       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14521     }
14522     else
14523     {
14524       continue;
14525     }
14526
14527     char *basename = getMusicInfoEntryFilename(music_nr);
14528
14529     if (basename == NULL)
14530       continue;
14531
14532     if (!music_info_listed(music_file_info, basename))
14533     {
14534       *new = get_music_file_info(basename, music_nr);
14535
14536       if (*new != NULL)
14537         new = &(*new)->next;
14538     }
14539   }
14540
14541   // get music file info for all remaining configured music files
14542   for (i = 0; i < num_music; i++)
14543   {
14544     music = getMusicListEntry(i);
14545
14546     if (music->filename == NULL)
14547       continue;
14548
14549     if (strEqual(music->filename, UNDEFINED_FILENAME))
14550       continue;
14551
14552     // a configured file may be not recognized as music
14553     if (!FileIsMusic(music->filename))
14554       continue;
14555
14556     if (!music_info_listed(music_file_info, music->filename))
14557     {
14558       *new = get_music_file_info(music->filename, i);
14559
14560       if (*new != NULL)
14561         new = &(*new)->next;
14562     }
14563   }
14564
14565   // get sound file info for all configured sound files
14566   for (i = 0; i < num_sounds; i++)
14567   {
14568     sound = getSoundListEntry(i);
14569
14570     if (sound->filename == NULL)
14571       continue;
14572
14573     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14574       continue;
14575
14576     // a configured file may be not recognized as sound
14577     if (!FileIsSound(sound->filename))
14578       continue;
14579
14580     if (!sound_info_listed(music_file_info, sound->filename))
14581     {
14582       *new = get_sound_file_info(sound->filename, i);
14583       if (*new != NULL)
14584         new = &(*new)->next;
14585     }
14586   }
14587
14588   // add pointers to previous list nodes
14589
14590   struct MusicFileInfo *node = music_file_info;
14591
14592   while (node != NULL)
14593   {
14594     if (node->next)
14595       node->next->prev = node;
14596
14597     node = node->next;
14598   }
14599 }
14600
14601 static void add_helpanim_entry(int element, int action, int direction,
14602                                int delay, int *num_list_entries)
14603 {
14604   struct HelpAnimInfo *new_list_entry;
14605   (*num_list_entries)++;
14606
14607   helpanim_info =
14608     checked_realloc(helpanim_info,
14609                     *num_list_entries * sizeof(struct HelpAnimInfo));
14610   new_list_entry = &helpanim_info[*num_list_entries - 1];
14611
14612   new_list_entry->element = element;
14613   new_list_entry->action = action;
14614   new_list_entry->direction = direction;
14615   new_list_entry->delay = delay;
14616 }
14617
14618 static void print_unknown_token(char *filename, char *token, int token_nr)
14619 {
14620   if (token_nr == 0)
14621   {
14622     Warn("---");
14623     Warn("unknown token(s) found in config file:");
14624     Warn("- config file: '%s'", filename);
14625   }
14626
14627   Warn("- token: '%s'", token);
14628 }
14629
14630 static void print_unknown_token_end(int token_nr)
14631 {
14632   if (token_nr > 0)
14633     Warn("---");
14634 }
14635
14636 void LoadHelpAnimInfo(void)
14637 {
14638   char *filename = getHelpAnimFilename();
14639   SetupFileList *setup_file_list = NULL, *list;
14640   SetupFileHash *element_hash, *action_hash, *direction_hash;
14641   int num_list_entries = 0;
14642   int num_unknown_tokens = 0;
14643   int i;
14644
14645   if (fileExists(filename))
14646     setup_file_list = loadSetupFileList(filename);
14647
14648   if (setup_file_list == NULL)
14649   {
14650     // use reliable default values from static configuration
14651     SetupFileList *insert_ptr;
14652
14653     insert_ptr = setup_file_list =
14654       newSetupFileList(helpanim_config[0].token,
14655                        helpanim_config[0].value);
14656
14657     for (i = 1; helpanim_config[i].token; i++)
14658       insert_ptr = addListEntry(insert_ptr,
14659                                 helpanim_config[i].token,
14660                                 helpanim_config[i].value);
14661   }
14662
14663   element_hash   = newSetupFileHash();
14664   action_hash    = newSetupFileHash();
14665   direction_hash = newSetupFileHash();
14666
14667   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14668     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14669
14670   for (i = 0; i < NUM_ACTIONS; i++)
14671     setHashEntry(action_hash, element_action_info[i].suffix,
14672                  i_to_a(element_action_info[i].value));
14673
14674   // do not store direction index (bit) here, but direction value!
14675   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14676     setHashEntry(direction_hash, element_direction_info[i].suffix,
14677                  i_to_a(1 << element_direction_info[i].value));
14678
14679   for (list = setup_file_list; list != NULL; list = list->next)
14680   {
14681     char *element_token, *action_token, *direction_token;
14682     char *element_value, *action_value, *direction_value;
14683     int delay = atoi(list->value);
14684
14685     if (strEqual(list->token, "end"))
14686     {
14687       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14688
14689       continue;
14690     }
14691
14692     /* first try to break element into element/action/direction parts;
14693        if this does not work, also accept combined "element[.act][.dir]"
14694        elements (like "dynamite.active"), which are unique elements */
14695
14696     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14697     {
14698       element_value = getHashEntry(element_hash, list->token);
14699       if (element_value != NULL)        // element found
14700         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14701                            &num_list_entries);
14702       else
14703       {
14704         // no further suffixes found -- this is not an element
14705         print_unknown_token(filename, list->token, num_unknown_tokens++);
14706       }
14707
14708       continue;
14709     }
14710
14711     // token has format "<prefix>.<something>"
14712
14713     action_token = strchr(list->token, '.');    // suffix may be action ...
14714     direction_token = action_token;             // ... or direction
14715
14716     element_token = getStringCopy(list->token);
14717     *strchr(element_token, '.') = '\0';
14718
14719     element_value = getHashEntry(element_hash, element_token);
14720
14721     if (element_value == NULL)          // this is no element
14722     {
14723       element_value = getHashEntry(element_hash, list->token);
14724       if (element_value != NULL)        // combined element found
14725         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14726                            &num_list_entries);
14727       else
14728         print_unknown_token(filename, list->token, num_unknown_tokens++);
14729
14730       free(element_token);
14731
14732       continue;
14733     }
14734
14735     action_value = getHashEntry(action_hash, action_token);
14736
14737     if (action_value != NULL)           // action found
14738     {
14739       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14740                     &num_list_entries);
14741
14742       free(element_token);
14743
14744       continue;
14745     }
14746
14747     direction_value = getHashEntry(direction_hash, direction_token);
14748
14749     if (direction_value != NULL)        // direction found
14750     {
14751       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14752                          &num_list_entries);
14753
14754       free(element_token);
14755
14756       continue;
14757     }
14758
14759     if (strchr(action_token + 1, '.') == NULL)
14760     {
14761       // no further suffixes found -- this is not an action nor direction
14762
14763       element_value = getHashEntry(element_hash, list->token);
14764       if (element_value != NULL)        // combined element found
14765         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14766                            &num_list_entries);
14767       else
14768         print_unknown_token(filename, list->token, num_unknown_tokens++);
14769
14770       free(element_token);
14771
14772       continue;
14773     }
14774
14775     // token has format "<prefix>.<suffix>.<something>"
14776
14777     direction_token = strchr(action_token + 1, '.');
14778
14779     action_token = getStringCopy(action_token);
14780     *strchr(action_token + 1, '.') = '\0';
14781
14782     action_value = getHashEntry(action_hash, action_token);
14783
14784     if (action_value == NULL)           // this is no action
14785     {
14786       element_value = getHashEntry(element_hash, list->token);
14787       if (element_value != NULL)        // combined element found
14788         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14789                            &num_list_entries);
14790       else
14791         print_unknown_token(filename, list->token, num_unknown_tokens++);
14792
14793       free(element_token);
14794       free(action_token);
14795
14796       continue;
14797     }
14798
14799     direction_value = getHashEntry(direction_hash, direction_token);
14800
14801     if (direction_value != NULL)        // direction found
14802     {
14803       add_helpanim_entry(atoi(element_value), atoi(action_value),
14804                          atoi(direction_value), delay, &num_list_entries);
14805
14806       free(element_token);
14807       free(action_token);
14808
14809       continue;
14810     }
14811
14812     // this is no direction
14813
14814     element_value = getHashEntry(element_hash, list->token);
14815     if (element_value != NULL)          // combined element found
14816       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14817                          &num_list_entries);
14818     else
14819       print_unknown_token(filename, list->token, num_unknown_tokens++);
14820
14821     free(element_token);
14822     free(action_token);
14823   }
14824
14825   print_unknown_token_end(num_unknown_tokens);
14826
14827   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14828   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14829
14830   freeSetupFileList(setup_file_list);
14831   freeSetupFileHash(element_hash);
14832   freeSetupFileHash(action_hash);
14833   freeSetupFileHash(direction_hash);
14834
14835 #if 0
14836   for (i = 0; i < num_list_entries; i++)
14837     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14838           EL_NAME(helpanim_info[i].element),
14839           helpanim_info[i].element,
14840           helpanim_info[i].action,
14841           helpanim_info[i].direction,
14842           helpanim_info[i].delay);
14843 #endif
14844 }
14845
14846 void LoadHelpTextInfo(void)
14847 {
14848   char *filename = getHelpTextFilename();
14849   int i;
14850
14851   if (helptext_info != NULL)
14852   {
14853     freeSetupFileHash(helptext_info);
14854     helptext_info = NULL;
14855   }
14856
14857   if (fileExists(filename))
14858     helptext_info = loadSetupFileHash(filename);
14859
14860   if (helptext_info == NULL)
14861   {
14862     // use reliable default values from static configuration
14863     helptext_info = newSetupFileHash();
14864
14865     for (i = 0; helptext_config[i].token; i++)
14866       setHashEntry(helptext_info,
14867                    helptext_config[i].token,
14868                    helptext_config[i].value);
14869   }
14870
14871 #if 0
14872   BEGIN_HASH_ITERATION(helptext_info, itr)
14873   {
14874     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14875           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14876   }
14877   END_HASH_ITERATION(hash, itr)
14878 #endif
14879 }
14880
14881
14882 // ----------------------------------------------------------------------------
14883 // convert levels
14884 // ----------------------------------------------------------------------------
14885
14886 #define MAX_NUM_CONVERT_LEVELS          1000
14887
14888 void ConvertLevels(void)
14889 {
14890   static LevelDirTree *convert_leveldir = NULL;
14891   static int convert_level_nr = -1;
14892   static int num_levels_handled = 0;
14893   static int num_levels_converted = 0;
14894   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14895   int i;
14896
14897   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14898                                                global.convert_leveldir);
14899
14900   if (convert_leveldir == NULL)
14901     Fail("no such level identifier: '%s'", global.convert_leveldir);
14902
14903   leveldir_current = convert_leveldir;
14904
14905   if (global.convert_level_nr != -1)
14906   {
14907     convert_leveldir->first_level = global.convert_level_nr;
14908     convert_leveldir->last_level  = global.convert_level_nr;
14909   }
14910
14911   convert_level_nr = convert_leveldir->first_level;
14912
14913   PrintLine("=", 79);
14914   Print("Converting levels\n");
14915   PrintLine("-", 79);
14916   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14917   Print("Level series name:       '%s'\n", convert_leveldir->name);
14918   Print("Level series author:     '%s'\n", convert_leveldir->author);
14919   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14920   PrintLine("=", 79);
14921   Print("\n");
14922
14923   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14924     levels_failed[i] = FALSE;
14925
14926   while (convert_level_nr <= convert_leveldir->last_level)
14927   {
14928     char *level_filename;
14929     boolean new_level;
14930
14931     level_nr = convert_level_nr++;
14932
14933     Print("Level %03d: ", level_nr);
14934
14935     LoadLevel(level_nr);
14936     if (level.no_level_file || level.no_valid_file)
14937     {
14938       Print("(no level)\n");
14939       continue;
14940     }
14941
14942     Print("converting level ... ");
14943
14944 #if 0
14945     // special case: conversion of some EMC levels as requested by ACME
14946     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14947 #endif
14948
14949     level_filename = getDefaultLevelFilename(level_nr);
14950     new_level = !fileExists(level_filename);
14951
14952     if (new_level)
14953     {
14954       SaveLevel(level_nr);
14955
14956       num_levels_converted++;
14957
14958       Print("converted.\n");
14959     }
14960     else
14961     {
14962       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14963         levels_failed[level_nr] = TRUE;
14964
14965       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14966     }
14967
14968     num_levels_handled++;
14969   }
14970
14971   Print("\n");
14972   PrintLine("=", 79);
14973   Print("Number of levels handled: %d\n", num_levels_handled);
14974   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14975          (num_levels_handled ?
14976           num_levels_converted * 100 / num_levels_handled : 0));
14977   PrintLine("-", 79);
14978   Print("Summary (for automatic parsing by scripts):\n");
14979   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14980          convert_leveldir->identifier, num_levels_converted,
14981          num_levels_handled,
14982          (num_levels_handled ?
14983           num_levels_converted * 100 / num_levels_handled : 0));
14984
14985   if (num_levels_handled != num_levels_converted)
14986   {
14987     Print(", FAILED:");
14988     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14989       if (levels_failed[i])
14990         Print(" %03d", i);
14991   }
14992
14993   Print("\n");
14994   PrintLine("=", 79);
14995
14996   CloseAllAndExit(0);
14997 }
14998
14999
15000 // ----------------------------------------------------------------------------
15001 // create and save images for use in level sketches (raw BMP format)
15002 // ----------------------------------------------------------------------------
15003
15004 void CreateLevelSketchImages(void)
15005 {
15006   Bitmap *bitmap1;
15007   Bitmap *bitmap2;
15008   int i;
15009
15010   InitElementPropertiesGfxElement();
15011
15012   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
15013   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
15014
15015   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
15016   {
15017     int element = getMappedElement(i);
15018     char basename1[16];
15019     char basename2[16];
15020     char *filename1;
15021     char *filename2;
15022
15023     sprintf(basename1, "%04d.bmp", i);
15024     sprintf(basename2, "%04ds.bmp", i);
15025
15026     filename1 = getPath2(global.create_sketch_images_dir, basename1);
15027     filename2 = getPath2(global.create_sketch_images_dir, basename2);
15028
15029     DrawSizedElement(0, 0, element, TILESIZE);
15030     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
15031
15032     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
15033       Fail("cannot save level sketch image file '%s'", filename1);
15034
15035     DrawSizedElement(0, 0, element, MINI_TILESIZE);
15036     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
15037
15038     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
15039       Fail("cannot save level sketch image file '%s'", filename2);
15040
15041     free(filename1);
15042     free(filename2);
15043
15044     // create corresponding SQL statements (for normal and small images)
15045     if (i < 1000)
15046     {
15047       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15048       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15049     }
15050
15051     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15052     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15053
15054     // optional: create content for forum level sketch demonstration post
15055     if (options.debug)
15056       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
15057   }
15058
15059   FreeBitmap(bitmap1);
15060   FreeBitmap(bitmap2);
15061
15062   if (options.debug)
15063     fprintf(stderr, "\n");
15064
15065   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15066
15067   CloseAllAndExit(0);
15068 }
15069
15070
15071 // ----------------------------------------------------------------------------
15072 // create and save images for element collecting animations (raw BMP format)
15073 // ----------------------------------------------------------------------------
15074
15075 static boolean createCollectImage(int element)
15076 {
15077   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15078 }
15079
15080 void CreateCollectElementImages(void)
15081 {
15082   int i, j;
15083   int num_steps = 8;
15084   int anim_frames = num_steps - 1;
15085   int tile_size = TILESIZE;
15086   int anim_width  = tile_size * anim_frames;
15087   int anim_height = tile_size;
15088   int num_collect_images = 0;
15089   int pos_collect_images = 0;
15090
15091   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15092     if (createCollectImage(i))
15093       num_collect_images++;
15094
15095   Info("Creating %d element collecting animation images ...",
15096        num_collect_images);
15097
15098   int dst_width  = anim_width * 2;
15099   int dst_height = anim_height * num_collect_images / 2;
15100   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15101   char *basename_bmp = "RocksCollect.bmp";
15102   char *basename_png = "RocksCollect.png";
15103   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15104   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15105   int len_filename_bmp = strlen(filename_bmp);
15106   int len_filename_png = strlen(filename_png);
15107   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15108   char cmd_convert[max_command_len];
15109
15110   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15111            filename_bmp,
15112            filename_png);
15113
15114   // force using RGBA surface for destination bitmap
15115   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15116                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15117
15118   dst_bitmap->surface =
15119     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15120
15121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15122   {
15123     if (!createCollectImage(i))
15124       continue;
15125
15126     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15127     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15128     int graphic = el2img(i);
15129     char *token_name = element_info[i].token_name;
15130     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15131     Bitmap *src_bitmap;
15132     int src_x, src_y;
15133
15134     Info("- creating collecting image for '%s' ...", token_name);
15135
15136     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15137
15138     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15139                tile_size, tile_size, 0, 0);
15140
15141     // force using RGBA surface for temporary bitmap (using transparent black)
15142     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15143                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15144
15145     tmp_bitmap->surface =
15146       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15147
15148     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15149
15150     for (j = 0; j < anim_frames; j++)
15151     {
15152       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15153       int frame_size = frame_size_final * num_steps;
15154       int offset = (tile_size - frame_size_final) / 2;
15155       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15156
15157       while (frame_size > frame_size_final)
15158       {
15159         frame_size /= 2;
15160
15161         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15162
15163         FreeBitmap(frame_bitmap);
15164
15165         frame_bitmap = half_bitmap;
15166       }
15167
15168       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15169                        frame_size_final, frame_size_final,
15170                        dst_x + j * tile_size + offset, dst_y + offset);
15171
15172       FreeBitmap(frame_bitmap);
15173     }
15174
15175     tmp_bitmap->surface_masked = NULL;
15176
15177     FreeBitmap(tmp_bitmap);
15178
15179     pos_collect_images++;
15180   }
15181
15182   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15183     Fail("cannot save element collecting image file '%s'", filename_bmp);
15184
15185   FreeBitmap(dst_bitmap);
15186
15187   Info("Converting image file from BMP to PNG ...");
15188
15189   if (system(cmd_convert) != 0)
15190     Fail("converting image file failed");
15191
15192   unlink(filename_bmp);
15193
15194   Info("Done.");
15195
15196   CloseAllAndExit(0);
15197 }
15198
15199
15200 // ----------------------------------------------------------------------------
15201 // create and save images for custom and group elements (raw BMP format)
15202 // ----------------------------------------------------------------------------
15203
15204 void CreateCustomElementImages(char *directory)
15205 {
15206   char *src_basename = "RocksCE-template.ilbm";
15207   char *dst_basename = "RocksCE.bmp";
15208   char *src_filename = getPath2(directory, src_basename);
15209   char *dst_filename = getPath2(directory, dst_basename);
15210   Bitmap *src_bitmap;
15211   Bitmap *bitmap;
15212   int yoffset_ce = 0;
15213   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15214   int i;
15215
15216   InitVideoDefaults();
15217
15218   ReCreateBitmap(&backbuffer, video.width, video.height);
15219
15220   src_bitmap = LoadImage(src_filename);
15221
15222   bitmap = CreateBitmap(TILEX * 16 * 2,
15223                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15224                         DEFAULT_DEPTH);
15225
15226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15227   {
15228     int x = i % 16;
15229     int y = i / 16;
15230     int ii = i + 1;
15231     int j;
15232
15233     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15234                TILEX * x, TILEY * y + yoffset_ce);
15235
15236     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15237                TILEX, TILEY,
15238                TILEX * x + TILEX * 16,
15239                TILEY * y + yoffset_ce);
15240
15241     for (j = 2; j >= 0; j--)
15242     {
15243       int c = ii % 10;
15244
15245       BlitBitmap(src_bitmap, bitmap,
15246                  TILEX + c * 7, 0, 6, 10,
15247                  TILEX * x + 6 + j * 7,
15248                  TILEY * y + 11 + yoffset_ce);
15249
15250       BlitBitmap(src_bitmap, bitmap,
15251                  TILEX + c * 8, TILEY, 6, 10,
15252                  TILEX * 16 + TILEX * x + 6 + j * 8,
15253                  TILEY * y + 10 + yoffset_ce);
15254
15255       ii /= 10;
15256     }
15257   }
15258
15259   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15260   {
15261     int x = i % 16;
15262     int y = i / 16;
15263     int ii = i + 1;
15264     int j;
15265
15266     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15267                TILEX * x, TILEY * y + yoffset_ge);
15268
15269     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15270                TILEX, TILEY,
15271                TILEX * x + TILEX * 16,
15272                TILEY * y + yoffset_ge);
15273
15274     for (j = 1; j >= 0; j--)
15275     {
15276       int c = ii % 10;
15277
15278       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15279                  TILEX * x + 6 + j * 10,
15280                  TILEY * y + 11 + yoffset_ge);
15281
15282       BlitBitmap(src_bitmap, bitmap,
15283                  TILEX + c * 8, TILEY + 12, 6, 10,
15284                  TILEX * 16 + TILEX * x + 10 + j * 8,
15285                  TILEY * y + 10 + yoffset_ge);
15286
15287       ii /= 10;
15288     }
15289   }
15290
15291   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15292     Fail("cannot save CE graphics file '%s'", dst_filename);
15293
15294   FreeBitmap(bitmap);
15295
15296   CloseAllAndExit(0);
15297 }