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