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