e15996394b425f80a04db6a99e767ecbe97c4c8f
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "editor.h"
23 #include "tools.h"
24 #include "tape.h"
25 #include "config.h"
26 #include "api.h"
27
28 #define ENABLE_UNUSED_CODE      0       // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
30 #define ENABLE_RESERVED_CODE    0       // reserved for later use
31
32 #define CHUNK_ID_LEN            4       // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
35
36 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
38
39 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
50
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
53
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
58
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
61
62 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
182     &li.time,                           100
183   },
184   {
185     -1,                                 SAVE_CONF_ALWAYS,
186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
187     &li.gems_needed,                    0
188   },
189   {
190     -1,                                 -1,
191     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
192     &li.random_seed,                    0
193   },
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204   {
205     -1,                                 -1,
206     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
207     &li.em_slippery_gems,               FALSE
208   },
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214   {
215     -1,                                 -1,
216     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
217     &li.can_move_into_acid_bits,        ~0      // default: everything can
218   },
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      // default: always deadly
223   },
224   {
225     -1,                                 -1,
226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
227     &li.em_explodes_by_fire,            FALSE
228   },
229   {
230     -1,                                 -1,
231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
232     &li.score[SC_TIME_BONUS],           1
233   },
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
237     &li.auto_exit_sokoban,              FALSE
238   },
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
242     &li.auto_count_gems,                FALSE
243   },
244   {
245     -1,                                 -1,
246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
247     &li.solved_by_one_player,           FALSE
248   },
249   {
250     -1,                                 -1,
251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
252     &li.time_score_base,                1
253   },
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
257     &li.rate_time_over_score,           FALSE
258   },
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
262     &li.bd_intermission,                FALSE
263   },
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
267     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
268   },
269   {
270     -1,                                 -1,
271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
272     &li.bd_pal_timing,                  FALSE
273   },
274   {
275     -1,                                 -1,
276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
277     &li.bd_cycle_delay_ms,              160
278   },
279   {
280     -1,                                 -1,
281     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
282     &li.bd_cycle_delay_c64,             0
283   },
284   {
285     -1,                                 -1,
286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
287     &li.bd_hatching_delay_cycles,       21
288   },
289   {
290     -1,                                 -1,
291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
292     &li.bd_hatching_delay_seconds,      2
293   },
294   {
295     -1,                                 -1,
296     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
297     &li.bd_line_shifting_borders,       FALSE
298   },
299   {
300     -1,                                 -1,
301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
302     &li.bd_scan_first_and_last_row,     TRUE
303   },
304   {
305     -1,                                 -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
307     &li.bd_short_explosions,            TRUE
308   },
309   {
310     -1,                                 -1,
311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
312     &li.bd_cave_random_seed_c64,        0
313   },
314   {
315     -1,                                 -1,
316     TYPE_INTEGER,                       CONF_VALUE_32_BIT(3),
317     &li.bd_color_b,                     GD_C64_COLOR(0)
318   },
319   {
320     -1,                                 -1,
321     TYPE_INTEGER,                       CONF_VALUE_32_BIT(4),
322     &li.bd_color_0,                     GD_C64_COLOR(0)
323   },
324   {
325     -1,                                 -1,
326     TYPE_INTEGER,                       CONF_VALUE_32_BIT(5),
327     &li.bd_color_1,                     GD_C64_COLOR(8)
328   },
329   {
330     -1,                                 -1,
331     TYPE_INTEGER,                       CONF_VALUE_32_BIT(6),
332     &li.bd_color_2,                     GD_C64_COLOR(11)
333   },
334   {
335     -1,                                 -1,
336     TYPE_INTEGER,                       CONF_VALUE_32_BIT(7),
337     &li.bd_color_3,                     GD_C64_COLOR(1)
338   },
339   {
340     -1,                                 -1,
341     TYPE_INTEGER,                       CONF_VALUE_32_BIT(8),
342     &li.bd_color_4,                     GD_C64_COLOR(5)
343   },
344   {
345     -1,                                 -1,
346     TYPE_INTEGER,                       CONF_VALUE_32_BIT(9),
347     &li.bd_color_5,                     GD_C64_COLOR(6)
348   },
349
350   {
351     -1,                                 -1,
352     -1,                                 -1,
353     NULL,                               -1
354   }
355 };
356
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
358 {
359   // (these values are the same for each player)
360   {
361     EL_PLAYER_1,                        -1,
362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
363     &li.block_last_field,               FALSE   // default case for EM levels
364   },
365   {
366     EL_PLAYER_1,                        -1,
367     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
368     &li.sp_block_last_field,            TRUE    // default case for SP levels
369   },
370   {
371     EL_PLAYER_1,                        -1,
372     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
373     &li.instant_relocation,             FALSE
374   },
375   {
376     EL_PLAYER_1,                        -1,
377     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
378     &li.can_pass_to_walkable,           FALSE
379   },
380   {
381     EL_PLAYER_1,                        -1,
382     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
383     &li.block_snap_field,               TRUE
384   },
385   {
386     EL_PLAYER_1,                        -1,
387     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
388     &li.continuous_snapping,            TRUE
389   },
390   {
391     EL_PLAYER_1,                        -1,
392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
393     &li.shifted_relocation,             FALSE
394   },
395   {
396     EL_PLAYER_1,                        -1,
397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
398     &li.lazy_relocation,                FALSE
399   },
400   {
401     EL_PLAYER_1,                        -1,
402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
403     &li.finish_dig_collect,             TRUE
404   },
405   {
406     EL_PLAYER_1,                        -1,
407     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
408     &li.keep_walkable_ce,               FALSE
409   },
410
411   // (these values are different for each player)
412   {
413     EL_PLAYER_1,                        -1,
414     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
415     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
416   },
417   {
418     EL_PLAYER_1,                        -1,
419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
420     &li.initial_player_gravity[0],      FALSE
421   },
422   {
423     EL_PLAYER_1,                        -1,
424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
425     &li.use_start_element[0],           FALSE
426   },
427   {
428     EL_PLAYER_1,                        -1,
429     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
430     &li.start_element[0],               EL_PLAYER_1
431   },
432   {
433     EL_PLAYER_1,                        -1,
434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
435     &li.use_artwork_element[0],         FALSE
436   },
437   {
438     EL_PLAYER_1,                        -1,
439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
440     &li.artwork_element[0],             EL_PLAYER_1
441   },
442   {
443     EL_PLAYER_1,                        -1,
444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
445     &li.use_explosion_element[0],       FALSE
446   },
447   {
448     EL_PLAYER_1,                        -1,
449     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
450     &li.explosion_element[0],           EL_PLAYER_1
451   },
452   {
453     EL_PLAYER_1,                        -1,
454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
455     &li.use_initial_inventory[0],       FALSE
456   },
457   {
458     EL_PLAYER_1,                        -1,
459     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
460     &li.initial_inventory_size[0],      1
461   },
462   {
463     EL_PLAYER_1,                        -1,
464     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
465     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
467   },
468
469   {
470     EL_PLAYER_2,                        -1,
471     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
472     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
473   },
474   {
475     EL_PLAYER_2,                        -1,
476     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
477     &li.initial_player_gravity[1],      FALSE
478   },
479   {
480     EL_PLAYER_2,                        -1,
481     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
482     &li.use_start_element[1],           FALSE
483   },
484   {
485     EL_PLAYER_2,                        -1,
486     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
487     &li.start_element[1],               EL_PLAYER_2
488   },
489   {
490     EL_PLAYER_2,                        -1,
491     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
492     &li.use_artwork_element[1],         FALSE
493   },
494   {
495     EL_PLAYER_2,                        -1,
496     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
497     &li.artwork_element[1],             EL_PLAYER_2
498   },
499   {
500     EL_PLAYER_2,                        -1,
501     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
502     &li.use_explosion_element[1],       FALSE
503   },
504   {
505     EL_PLAYER_2,                        -1,
506     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
507     &li.explosion_element[1],           EL_PLAYER_2
508   },
509   {
510     EL_PLAYER_2,                        -1,
511     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
512     &li.use_initial_inventory[1],       FALSE
513   },
514   {
515     EL_PLAYER_2,                        -1,
516     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
517     &li.initial_inventory_size[1],      1
518   },
519   {
520     EL_PLAYER_2,                        -1,
521     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
522     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
524   },
525
526   {
527     EL_PLAYER_3,                        -1,
528     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
529     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
530   },
531   {
532     EL_PLAYER_3,                        -1,
533     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
534     &li.initial_player_gravity[2],      FALSE
535   },
536   {
537     EL_PLAYER_3,                        -1,
538     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
539     &li.use_start_element[2],           FALSE
540   },
541   {
542     EL_PLAYER_3,                        -1,
543     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
544     &li.start_element[2],               EL_PLAYER_3
545   },
546   {
547     EL_PLAYER_3,                        -1,
548     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
549     &li.use_artwork_element[2],         FALSE
550   },
551   {
552     EL_PLAYER_3,                        -1,
553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
554     &li.artwork_element[2],             EL_PLAYER_3
555   },
556   {
557     EL_PLAYER_3,                        -1,
558     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
559     &li.use_explosion_element[2],       FALSE
560   },
561   {
562     EL_PLAYER_3,                        -1,
563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
564     &li.explosion_element[2],           EL_PLAYER_3
565   },
566   {
567     EL_PLAYER_3,                        -1,
568     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
569     &li.use_initial_inventory[2],       FALSE
570   },
571   {
572     EL_PLAYER_3,                        -1,
573     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
574     &li.initial_inventory_size[2],      1
575   },
576   {
577     EL_PLAYER_3,                        -1,
578     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
579     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
581   },
582
583   {
584     EL_PLAYER_4,                        -1,
585     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
586     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
587   },
588   {
589     EL_PLAYER_4,                        -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
591     &li.initial_player_gravity[3],      FALSE
592   },
593   {
594     EL_PLAYER_4,                        -1,
595     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
596     &li.use_start_element[3],           FALSE
597   },
598   {
599     EL_PLAYER_4,                        -1,
600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
601     &li.start_element[3],               EL_PLAYER_4
602   },
603   {
604     EL_PLAYER_4,                        -1,
605     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
606     &li.use_artwork_element[3],         FALSE
607   },
608   {
609     EL_PLAYER_4,                        -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
611     &li.artwork_element[3],             EL_PLAYER_4
612   },
613   {
614     EL_PLAYER_4,                        -1,
615     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
616     &li.use_explosion_element[3],       FALSE
617   },
618   {
619     EL_PLAYER_4,                        -1,
620     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
621     &li.explosion_element[3],           EL_PLAYER_4
622   },
623   {
624     EL_PLAYER_4,                        -1,
625     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
626     &li.use_initial_inventory[3],       FALSE
627   },
628   {
629     EL_PLAYER_4,                        -1,
630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
631     &li.initial_inventory_size[3],      1
632   },
633   {
634     EL_PLAYER_4,                        -1,
635     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
636     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
638   },
639
640   // (these values are only valid for BD style levels)
641   // (some values for BD style amoeba following below)
642   {
643     EL_BDX_PLAYER,                      -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BDX_PLAYER,                      -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BDX_PLAYER,                      -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BDX_PLAYER,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BDX_PLAYER,                      -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BDX_PLAYER,                      -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BDX_SAND_1,                      -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BDX_SAND_1
677   },
678
679   {
680     EL_BDX_ROCK,                        -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BDX_ROCK_FALLING
683   },
684   {
685     EL_BDX_ROCK,                        -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BDX_ROCK
688   },
689
690   {
691     EL_BDX_DIAMOND,                     -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BDX_DIAMOND,                     -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
699   },
700   {
701     EL_BDX_DIAMOND,                     -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BDX_DIAMOND
704   },
705
706   {
707     EL_BDX_FIREFLY_1,                   -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_1_explodes_to,       EL_BDX_EXPLODING_1
710   },
711
712   {
713     EL_BDX_FIREFLY_2,                   -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BDX_EXPLODING_1
716   },
717
718   {
719     EL_BDX_BUTTERFLY_1,                 -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_1_explodes_to,     EL_BDX_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BDX_BUTTERFLY_2,                 -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BDX_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BDX_STONEFLY,                    -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BDX_ROCK_GROWING_1
734   },
735
736   {
737     EL_BDX_DRAGONFLY,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BDX_EXPLODING_1
740   },
741
742   {
743     EL_BDX_DIAMOND_GROWING_5,   -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BDX_DIAMOND
746   },
747
748   {
749     EL_BDX_BOMB_EXPLODING_4,            -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BDX_WALL
752   },
753
754   {
755     EL_BDX_NITRO_PACK_EXPLODING_4,      -1,
756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
757     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
758   },
759
760   {
761     EL_BDX_EXPLODING_5,                 -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BDX_MAGIC_WALL,                  -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BDX_MAGIC_WALL,                  -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BDX_MAGIC_WALL,                  -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BDX_MAGIC_WALL,                  -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BDX_MAGIC_WALL,                  -1,
788     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_time,             999
790   },
791   {
792     EL_BDX_MAGIC_WALL,                  -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_diamond_to,       EL_BDX_ROCK_FALLING
795   },
796   {
797     EL_BDX_MAGIC_WALL,                  -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_rock_to,          EL_BDX_DIAMOND_FALLING
800   },
801   {
802     EL_BDX_MAGIC_WALL,                  -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_mega_rock_to,     EL_BDX_NITRO_PACK_FALLING
805   },
806   {
807     EL_BDX_MAGIC_WALL,                  -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nut_to,           EL_BDX_NUT_FALLING
810   },
811   {
812     EL_BDX_MAGIC_WALL,                  -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_nitro_pack_to,    EL_BDX_MEGA_ROCK_FALLING
815   },
816   {
817     EL_BDX_MAGIC_WALL,                  -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
820   },
821   {
822     EL_BDX_MAGIC_WALL,                  -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
824     &li.bd_magic_wall_flying_rock_to,   EL_BDX_FLYING_DIAMOND_FLYING
825   },
826
827   {
828     EL_BDX_CLOCK,                       -1,
829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
830     &li.bd_clock_extra_time,            30
831   },
832
833   {
834     EL_BDX_VOODOO_DOLL,                 -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.bd_voodoo_collects_diamonds,    FALSE
837   },
838   {
839     EL_BDX_VOODOO_DOLL,                 -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
841     &li.bd_voodoo_hurt_kills_player,    FALSE
842   },
843   {
844     EL_BDX_VOODOO_DOLL,                 -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
846     &li.bd_voodoo_dies_by_rock,         FALSE
847   },
848   {
849     EL_BDX_VOODOO_DOLL,                 -1,
850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
851     &li.bd_voodoo_vanish_by_explosion,  TRUE
852   },
853   {
854     EL_BDX_VOODOO_DOLL,                 -1,
855     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
856     &li.bd_voodoo_penalty_time,         30
857   },
858
859   {
860     EL_BDX_SLIME,                       -1,
861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
862     &li.bd_slime_is_predictable,        TRUE
863   },
864   {
865     EL_BDX_SLIME,                       -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
867     &li.bd_slime_permeability_rate,     100
868   },
869   {
870     EL_BDX_SLIME,                       -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
872     &li.bd_slime_permeability_bits_c64, 0
873   },
874   {
875     EL_BDX_SLIME,                       -1,
876     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
877     &li.bd_slime_random_seed_c64,       -1
878   },
879   {
880     EL_BDX_SLIME,                       -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
882     &li.bd_slime_eats_element_1,        EL_BDX_DIAMOND
883   },
884   {
885     EL_BDX_SLIME,                       -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
887     &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
888   },
889   {
890     EL_BDX_SLIME,                       -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
892     &li.bd_slime_eats_element_2,        EL_BDX_ROCK
893   },
894   {
895     EL_BDX_SLIME,                       -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
897     &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
898   },
899   {
900     EL_BDX_SLIME,                       -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
902     &li.bd_slime_eats_element_3,        EL_BDX_NUT
903   },
904   {
905     EL_BDX_SLIME,                       -1,
906     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
907     &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
908   },
909
910   {
911     EL_BDX_ACID,                        -1,
912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
913     &li.bd_acid_eats_element,           EL_BDX_SAND_1
914   },
915   {
916     EL_BDX_ACID,                        -1,
917     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
918     &li.bd_acid_spread_rate,            3
919   },
920   {
921     EL_BDX_ACID,                        -1,
922     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
923     &li.bd_acid_turns_to_element,       EL_BDX_EXPLODING_3
924   },
925
926   {
927     EL_BDX_BITER,                       -1,
928     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
929     &li.bd_biter_move_delay,            0
930   },
931   {
932     EL_BDX_BITER,                       -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_biter_eats_element,          EL_BDX_DIAMOND
935   },
936
937   {
938     EL_BDX_BLADDER,                     -1,
939     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
940     &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
941   },
942
943   {
944     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
946     &li.bd_change_expanding_wall,       FALSE
947   },
948   {
949     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
951     &li.bd_expanding_wall_looks_like,   EL_BDX_WALL
952   },
953
954   {
955     EL_BDX_REPLICATOR,                  -1,
956     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
957     &li.bd_replicators_active,          TRUE
958   },
959   {
960     EL_BDX_REPLICATOR,                  -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
962     &li.bd_replicator_create_delay,     4
963   },
964
965   {
966     EL_BDX_CONVEYOR_LEFT,               -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
968     &li.bd_conveyor_belts_active,       TRUE
969   },
970   {
971     EL_BDX_CONVEYOR_LEFT,               -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
973     &li.bd_conveyor_belts_changed,      FALSE
974   },
975
976   {
977     EL_BDX_WATER,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.bd_water_cannot_flow_down,      FALSE
980   },
981
982   {
983     EL_BDX_NUT,                         -1,
984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
985     &li.bd_nut_content,                 EL_BDX_NUT_BREAKING_1
986   },
987
988   {
989     EL_BDX_PNEUMATIC_HAMMER,            -1,
990     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
991     &li.bd_hammer_walls_break_delay,    5
992   },
993   {
994     EL_BDX_PNEUMATIC_HAMMER,            -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
996     &li.bd_hammer_walls_reappear,       FALSE
997   },
998   {
999     EL_BDX_PNEUMATIC_HAMMER,            -1,
1000     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1001     &li.bd_hammer_walls_reappear_delay, 100
1002   },
1003
1004   {
1005     EL_BDX_ROCKET_LAUNCHER,             -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1007     &li.bd_infinite_rockets,            FALSE
1008   },
1009
1010   {
1011     EL_BDX_SKELETON,                    -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1013     &li.bd_num_skeletons_needed_for_pot, 5
1014   },
1015   {
1016     EL_BDX_SKELETON,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1018     &li.bd_skeleton_worth_num_diamonds, 0
1019   },
1020
1021   {
1022     EL_BDX_CREATURE_SWITCH,             -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1024     &li.bd_creatures_start_backwards,   FALSE
1025   },
1026   {
1027     EL_BDX_CREATURE_SWITCH,             -1,
1028     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1029     &li.bd_creatures_turn_on_hatching,  FALSE
1030   },
1031   {
1032     EL_BDX_CREATURE_SWITCH,             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.bd_creatures_auto_turn_delay,   0
1035   },
1036
1037   {
1038     EL_BDX_GRAVITY_SWITCH,              -1,
1039     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1040     &li.bd_gravity_direction,           GD_MV_DOWN
1041   },
1042   {
1043     EL_BDX_GRAVITY_SWITCH,              -1,
1044     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1045     &li.bd_gravity_switch_active,       FALSE
1046   },
1047   {
1048     EL_BDX_GRAVITY_SWITCH,              -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1050     &li.bd_gravity_switch_delay,        10
1051   },
1052   {
1053     EL_BDX_GRAVITY_SWITCH,              -1,
1054     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1055     &li.bd_gravity_affects_all,         TRUE
1056   },
1057
1058   // (the following values are related to various game elements)
1059
1060   {
1061     EL_EMERALD,                         -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_EMERALD],              10
1064   },
1065
1066   {
1067     EL_DIAMOND,                         -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_DIAMOND],              10
1070   },
1071
1072   {
1073     EL_BUG,                             -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.score[SC_BUG],                  10
1076   },
1077
1078   {
1079     EL_SPACESHIP,                       -1,
1080     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1081     &li.score[SC_SPACESHIP],            10
1082   },
1083
1084   {
1085     EL_PACMAN,                          -1,
1086     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1087     &li.score[SC_PACMAN],               10
1088   },
1089
1090   {
1091     EL_NUT,                             -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1093     &li.score[SC_NUT],                  10
1094   },
1095
1096   {
1097     EL_DYNAMITE,                        -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.score[SC_DYNAMITE],             10
1100   },
1101
1102   {
1103     EL_KEY_1,                           -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.score[SC_KEY],                  10
1106   },
1107
1108   {
1109     EL_PEARL,                           -1,
1110     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1111     &li.score[SC_PEARL],                10
1112   },
1113
1114   {
1115     EL_CRYSTAL,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.score[SC_CRYSTAL],              10
1118   },
1119
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1123     &li.amoeba_content,                 EL_DIAMOND
1124   },
1125   {
1126     EL_BD_AMOEBA,                       -1,
1127     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1128     &li.amoeba_speed,                   10
1129   },
1130   {
1131     EL_BD_AMOEBA,                       -1,
1132     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1133     &li.grow_into_diggable,             TRUE
1134   },
1135
1136   {
1137     EL_BDX_AMOEBA_1,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.bd_amoeba_1_threshold_too_big,  200
1140   },
1141   {
1142     EL_BDX_AMOEBA_1,                    -1,
1143     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1144     &li.bd_amoeba_1_slow_growth_time,   200
1145   },
1146   {
1147     EL_BDX_AMOEBA_1,                    -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1149     &li.bd_amoeba_1_content_too_big,    EL_BDX_ROCK
1150   },
1151   {
1152     EL_BDX_AMOEBA_1,                    -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1154     &li.bd_amoeba_1_content_enclosed,   EL_BDX_DIAMOND
1155   },
1156   {
1157     EL_BDX_AMOEBA_1,                    -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1159     &li.bd_amoeba_1_slow_growth_rate,   3
1160   },
1161   {
1162     EL_BDX_AMOEBA_1,                    -1,
1163     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1164     &li.bd_amoeba_1_fast_growth_rate,   25
1165   },
1166   {
1167     EL_BDX_AMOEBA_1,                    -1,
1168     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1169     &li.bd_amoeba_wait_for_hatching,    FALSE
1170   },
1171   {
1172     EL_BDX_AMOEBA_1,                    -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1174     &li.bd_amoeba_start_immediately,    TRUE
1175   },
1176
1177   {
1178     EL_BDX_AMOEBA_2,                    -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1180     &li.bd_amoeba_2_threshold_too_big,  200
1181   },
1182   {
1183     EL_BDX_AMOEBA_2,                    -1,
1184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1185     &li.bd_amoeba_2_slow_growth_time,   200
1186   },
1187   {
1188     EL_BDX_AMOEBA_2,                    -1,
1189     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1190     &li.bd_amoeba_2_content_too_big,    EL_BDX_ROCK
1191   },
1192   {
1193     EL_BDX_AMOEBA_2,                    -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1195     &li.bd_amoeba_2_content_enclosed,   EL_BDX_DIAMOND
1196   },
1197   {
1198     EL_BDX_AMOEBA_2,                    -1,
1199     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1200     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1201   },
1202   {
1203     EL_BDX_AMOEBA_2,                    -1,
1204     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1205     &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1206   },
1207   {
1208     EL_BDX_AMOEBA_2,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1210     &li.bd_amoeba_2_slow_growth_rate,   3
1211   },
1212   {
1213     EL_BDX_AMOEBA_2,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1215     &li.bd_amoeba_2_fast_growth_rate,   25
1216   },
1217   {
1218     EL_BDX_AMOEBA_2,                    -1,
1219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1220     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1221   },
1222
1223   {
1224     EL_YAMYAM,                          -1,
1225     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.yamyam_content,                 EL_ROCK, NULL,
1227     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1228   },
1229   {
1230     EL_YAMYAM,                          -1,
1231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1232     &li.score[SC_YAMYAM],               10
1233   },
1234
1235   {
1236     EL_ROBOT,                           -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.score[SC_ROBOT],                10
1239   },
1240   {
1241     EL_ROBOT,                           -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1243     &li.slurp_score,                    10
1244   },
1245
1246   {
1247     EL_ROBOT_WHEEL,                     -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.time_wheel,                     10
1250   },
1251
1252   {
1253     EL_MAGIC_WALL,                      -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.time_magic_wall,                10
1256   },
1257
1258   {
1259     EL_GAME_OF_LIFE,                    -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.game_of_life[0],                2
1262   },
1263   {
1264     EL_GAME_OF_LIFE,                    -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &li.game_of_life[1],                3
1267   },
1268   {
1269     EL_GAME_OF_LIFE,                    -1,
1270     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1271     &li.game_of_life[2],                3
1272   },
1273   {
1274     EL_GAME_OF_LIFE,                    -1,
1275     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1276     &li.game_of_life[3],                3
1277   },
1278   {
1279     EL_GAME_OF_LIFE,                    -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1281     &li.use_life_bugs,                  FALSE
1282   },
1283
1284   {
1285     EL_BIOMAZE,                         -1,
1286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1287     &li.biomaze[0],                     2
1288   },
1289   {
1290     EL_BIOMAZE,                         -1,
1291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1292     &li.biomaze[1],                     3
1293   },
1294   {
1295     EL_BIOMAZE,                         -1,
1296     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1297     &li.biomaze[2],                     3
1298   },
1299   {
1300     EL_BIOMAZE,                         -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1302     &li.biomaze[3],                     3
1303   },
1304
1305   {
1306     EL_TIMEGATE_SWITCH,                 -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_timegate,                  10
1309   },
1310
1311   {
1312     EL_LIGHT_SWITCH_ACTIVE,             -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.time_light,                     10
1315   },
1316
1317   {
1318     EL_SHIELD_NORMAL,                   -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.shield_normal_time,             10
1321   },
1322   {
1323     EL_SHIELD_NORMAL,                   -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.score[SC_SHIELD],               10
1326   },
1327
1328   {
1329     EL_SHIELD_DEADLY,                   -1,
1330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1331     &li.shield_deadly_time,             10
1332   },
1333   {
1334     EL_SHIELD_DEADLY,                   -1,
1335     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1336     &li.score[SC_SHIELD],               10
1337   },
1338
1339   {
1340     EL_EXTRA_TIME,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1342     &li.extra_time,                     10
1343   },
1344   {
1345     EL_EXTRA_TIME,                      -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1347     &li.extra_time_score,               10
1348   },
1349
1350   {
1351     EL_TIME_ORB_FULL,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.time_orb_time,                  10
1354   },
1355   {
1356     EL_TIME_ORB_FULL,                   -1,
1357     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1358     &li.use_time_orb_bug,               FALSE
1359   },
1360
1361   {
1362     EL_SPRING,                          -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.use_spring_bug,                 FALSE
1365   },
1366
1367   {
1368     EL_EMC_ANDROID,                     -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.android_move_time,              10
1371   },
1372   {
1373     EL_EMC_ANDROID,                     -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1375     &li.android_clone_time,             10
1376   },
1377   {
1378     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1379     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.android_clone_element[0],       EL_EMPTY, NULL,
1381     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1382   },
1383   {
1384     EL_EMC_ANDROID,                     -1,
1385     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1386     &li.android_clone_element[0],       EL_EMPTY, NULL,
1387     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1388   },
1389
1390   {
1391     EL_EMC_LENSES,                      -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1393     &li.lenses_score,                   10
1394   },
1395   {
1396     EL_EMC_LENSES,                      -1,
1397     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1398     &li.lenses_time,                    10
1399   },
1400
1401   {
1402     EL_EMC_MAGNIFIER,                   -1,
1403     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1404     &li.magnify_score,                  10
1405   },
1406   {
1407     EL_EMC_MAGNIFIER,                   -1,
1408     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1409     &li.magnify_time,                   10
1410   },
1411
1412   {
1413     EL_EMC_MAGIC_BALL,                  -1,
1414     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1415     &li.ball_time,                      10
1416   },
1417   {
1418     EL_EMC_MAGIC_BALL,                  -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.ball_random,                    FALSE
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL,                  -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.ball_active_initial,            FALSE
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL,                  -1,
1429     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1430     &li.ball_content,                   EL_EMPTY, NULL,
1431     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1432   },
1433
1434   {
1435     EL_SOKOBAN_FIELD_EMPTY,             -1,
1436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1437     &li.sb_fields_needed,               TRUE
1438   },
1439
1440   {
1441     EL_SOKOBAN_OBJECT,                  -1,
1442     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1443     &li.sb_objects_needed,              TRUE
1444   },
1445
1446   {
1447     EL_MM_MCDUFFIN,                     -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &li.mm_laser_red,                   FALSE
1450   },
1451   {
1452     EL_MM_MCDUFFIN,                     -1,
1453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1454     &li.mm_laser_green,                 FALSE
1455   },
1456   {
1457     EL_MM_MCDUFFIN,                     -1,
1458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1459     &li.mm_laser_blue,                  TRUE
1460   },
1461
1462   {
1463     EL_DF_LASER,                        -1,
1464     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1465     &li.df_laser_red,                   TRUE
1466   },
1467   {
1468     EL_DF_LASER,                        -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1470     &li.df_laser_green,                 TRUE
1471   },
1472   {
1473     EL_DF_LASER,                        -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1475     &li.df_laser_blue,                  FALSE
1476   },
1477
1478   {
1479     EL_MM_FUSE_ACTIVE,                  -1,
1480     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1481     &li.mm_time_fuse,                   25
1482   },
1483   {
1484     EL_MM_BOMB,                         -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1486     &li.mm_time_bomb,                   75
1487   },
1488
1489   {
1490     EL_MM_GRAY_BALL,                    -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1492     &li.mm_time_ball,                   75
1493   },
1494   {
1495     EL_MM_GRAY_BALL,                    -1,
1496     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1497     &li.mm_ball_choice_mode,            ANIM_RANDOM
1498   },
1499   {
1500     EL_MM_GRAY_BALL,                    -1,
1501     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1502     &li.mm_ball_content,                EL_EMPTY, NULL,
1503     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1504   },
1505   {
1506     EL_MM_GRAY_BALL,                    -1,
1507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1508     &li.rotate_mm_ball_content,         TRUE
1509   },
1510   {
1511     EL_MM_GRAY_BALL,                    -1,
1512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1513     &li.explode_mm_ball,                FALSE
1514   },
1515
1516   {
1517     EL_MM_STEEL_BLOCK,                  -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1519     &li.mm_time_block,                  75
1520   },
1521   {
1522     EL_MM_LIGHTBALL,                    -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1524     &li.score[SC_ELEM_BONUS],           10
1525   },
1526
1527   {
1528     -1,                                 -1,
1529     -1,                                 -1,
1530     NULL,                               -1
1531   }
1532 };
1533
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1535 {
1536   {
1537     -1,                                 -1,
1538     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1539     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1544     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1550     &xx_envelope.autowrap,              FALSE
1551   },
1552   {
1553     -1,                                 -1,
1554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1555     &xx_envelope.centered,              FALSE
1556   },
1557
1558   {
1559     -1,                                 -1,
1560     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1561     &xx_envelope.text,                  -1, NULL,
1562     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1563     &xx_default_string_empty[0]
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     -1,                                 -1,
1569     NULL,                               -1
1570   }
1571 };
1572
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1574 {
1575   {
1576     -1,                                 -1,
1577     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1578     &xx_ei.description[0],              -1,
1579     &yy_ei.description[0],
1580     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1581     &xx_default_description[0]
1582   },
1583
1584   {
1585     -1,                                 -1,
1586     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1587     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1589   },
1590 #if ENABLE_RESERVED_CODE
1591   // (reserved for later use)
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1595     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1597   },
1598 #endif
1599
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1603     &xx_ei.use_gfx_element,             FALSE,
1604     &yy_ei.use_gfx_element
1605   },
1606   {
1607     -1,                                 -1,
1608     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1609     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1610     &yy_ei.gfx_element_initial
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1616     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1617     &yy_ei.access_direction
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1623     &xx_ei.collect_score_initial,       10,
1624     &yy_ei.collect_score_initial
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1629     &xx_ei.collect_count_initial,       1,
1630     &yy_ei.collect_count_initial
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1636     &xx_ei.ce_value_fixed_initial,      0,
1637     &yy_ei.ce_value_fixed_initial
1638   },
1639   {
1640     -1,                                 -1,
1641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1642     &xx_ei.ce_value_random_initial,     0,
1643     &yy_ei.ce_value_random_initial
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1648     &xx_ei.use_last_ce_value,           FALSE,
1649     &yy_ei.use_last_ce_value
1650   },
1651
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1655     &xx_ei.push_delay_fixed,            8,
1656     &yy_ei.push_delay_fixed
1657   },
1658   {
1659     -1,                                 -1,
1660     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1661     &xx_ei.push_delay_random,           8,
1662     &yy_ei.push_delay_random
1663   },
1664   {
1665     -1,                                 -1,
1666     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1667     &xx_ei.drop_delay_fixed,            0,
1668     &yy_ei.drop_delay_fixed
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1673     &xx_ei.drop_delay_random,           0,
1674     &yy_ei.drop_delay_random
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1679     &xx_ei.move_delay_fixed,            0,
1680     &yy_ei.move_delay_fixed
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1685     &xx_ei.move_delay_random,           0,
1686     &yy_ei.move_delay_random
1687   },
1688   {
1689     -1,                                 -1,
1690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1691     &xx_ei.step_delay_fixed,            0,
1692     &yy_ei.step_delay_fixed
1693   },
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1697     &xx_ei.step_delay_random,           0,
1698     &yy_ei.step_delay_random
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1704     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1705     &yy_ei.move_pattern
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1710     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1711     &yy_ei.move_direction_initial
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1716     &xx_ei.move_stepsize,               TILEX / 8,
1717     &yy_ei.move_stepsize
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1723     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1724     &yy_ei.move_enter_element
1725   },
1726   {
1727     -1,                                 -1,
1728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1729     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1730     &yy_ei.move_leave_element
1731   },
1732   {
1733     -1,                                 -1,
1734     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1735     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1736     &yy_ei.move_leave_type
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1742     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1743     &yy_ei.slippery_type
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1749     &xx_ei.explosion_type,              EXPLODES_3X3,
1750     &yy_ei.explosion_type
1751   },
1752   {
1753     -1,                                 -1,
1754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1755     &xx_ei.explosion_delay,             16,
1756     &yy_ei.explosion_delay
1757   },
1758   {
1759     -1,                                 -1,
1760     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1761     &xx_ei.ignition_delay,              8,
1762     &yy_ei.ignition_delay
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1768     &xx_ei.content,                     EL_EMPTY_SPACE,
1769     &yy_ei.content,
1770     &xx_num_contents,                   1, 1
1771   },
1772
1773   // ---------- "num_change_pages" must be the last entry ---------------------
1774
1775   {
1776     -1,                                 SAVE_CONF_ALWAYS,
1777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1778     &xx_ei.num_change_pages,            1,
1779     &yy_ei.num_change_pages
1780   },
1781
1782   {
1783     -1,                                 -1,
1784     -1,                                 -1,
1785     NULL,                               -1,
1786     NULL
1787   }
1788 };
1789
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1791 {
1792   // ---------- "current_change_page" must be the first entry -----------------
1793
1794   {
1795     -1,                                 SAVE_CONF_ALWAYS,
1796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1797     &xx_current_change_page,            -1
1798   },
1799
1800   // ---------- (the remaining entries can be in any order) -------------------
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1805     &xx_change.can_change,              FALSE
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1811     &xx_event_bits[0],                  0
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1816     &xx_event_bits[1],                  0
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1822     &xx_change.trigger_player,          CH_PLAYER_ANY
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1827     &xx_change.trigger_side,            CH_SIDE_ANY
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1832     &xx_change.trigger_page,            CH_PAGE_ANY
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1838     &xx_change.target_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1844     &xx_change.delay_fixed,             0
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1849     &xx_change.delay_random,            0
1850   },
1851   {
1852     -1,                                 -1,
1853     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1854     &xx_change.delay_frames,            FRAMES_PER_SECOND
1855   },
1856
1857   {
1858     -1,                                 -1,
1859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1860     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861   },
1862
1863   {
1864     -1,                                 -1,
1865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1866     &xx_change.explode,                 FALSE
1867   },
1868   {
1869     -1,                                 -1,
1870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1871     &xx_change.use_target_content,      FALSE
1872   },
1873   {
1874     -1,                                 -1,
1875     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1876     &xx_change.only_if_complete,        FALSE
1877   },
1878   {
1879     -1,                                 -1,
1880     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1881     &xx_change.use_random_replace,      FALSE
1882   },
1883   {
1884     -1,                                 -1,
1885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1886     &xx_change.random_percentage,       100
1887   },
1888   {
1889     -1,                                 -1,
1890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1891     &xx_change.replace_when,            CP_WHEN_EMPTY
1892   },
1893
1894   {
1895     -1,                                 -1,
1896     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1897     &xx_change.has_action,              FALSE
1898   },
1899   {
1900     -1,                                 -1,
1901     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1902     &xx_change.action_type,             CA_NO_ACTION
1903   },
1904   {
1905     -1,                                 -1,
1906     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1907     &xx_change.action_mode,             CA_MODE_UNDEFINED
1908   },
1909   {
1910     -1,                                 -1,
1911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1912     &xx_change.action_arg,              CA_ARG_UNDEFINED
1913   },
1914
1915   {
1916     -1,                                 -1,
1917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1918     &xx_change.action_element,          EL_EMPTY_SPACE
1919   },
1920
1921   {
1922     -1,                                 -1,
1923     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1924     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1925     &xx_num_contents,                   1, 1
1926   },
1927
1928   {
1929     -1,                                 -1,
1930     -1,                                 -1,
1931     NULL,                               -1
1932   }
1933 };
1934
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1936 {
1937   {
1938     -1,                                 -1,
1939     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1940     &xx_ei.description[0],              -1, NULL,
1941     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1942     &xx_default_description[0]
1943   },
1944
1945   {
1946     -1,                                 -1,
1947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1948     &xx_ei.use_gfx_element,             FALSE
1949   },
1950   {
1951     -1,                                 -1,
1952     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1953     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1954   },
1955
1956   {
1957     -1,                                 -1,
1958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1959     &xx_group.choice_mode,              ANIM_RANDOM
1960   },
1961
1962   {
1963     -1,                                 -1,
1964     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1965     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1966     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1967   },
1968
1969   {
1970     -1,                                 -1,
1971     -1,                                 -1,
1972     NULL,                               -1
1973   }
1974 };
1975
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1977 {
1978   {
1979     -1,                                 -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1981     &xx_ei.use_gfx_element,             FALSE
1982   },
1983   {
1984     -1,                                 -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1986     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1987   },
1988
1989   {
1990     -1,                                 -1,
1991     -1,                                 -1,
1992     NULL,                               -1
1993   }
1994 };
1995
1996 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1997 {
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
2001     &li.block_snap_field,               TRUE
2002   },
2003   {
2004     EL_PLAYER_1,                        -1,
2005     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2006     &li.continuous_snapping,            TRUE
2007   },
2008   {
2009     EL_PLAYER_1,                        -1,
2010     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2011     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2012   },
2013   {
2014     EL_PLAYER_1,                        -1,
2015     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2016     &li.use_start_element[0],           FALSE
2017   },
2018   {
2019     EL_PLAYER_1,                        -1,
2020     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2021     &li.start_element[0],               EL_PLAYER_1
2022   },
2023   {
2024     EL_PLAYER_1,                        -1,
2025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2026     &li.use_artwork_element[0],         FALSE
2027   },
2028   {
2029     EL_PLAYER_1,                        -1,
2030     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2031     &li.artwork_element[0],             EL_PLAYER_1
2032   },
2033   {
2034     EL_PLAYER_1,                        -1,
2035     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2036     &li.use_explosion_element[0],       FALSE
2037   },
2038   {
2039     EL_PLAYER_1,                        -1,
2040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2041     &li.explosion_element[0],           EL_PLAYER_1
2042   },
2043
2044   {
2045     -1,                                 -1,
2046     -1,                                 -1,
2047     NULL,                               -1
2048   }
2049 };
2050
2051 static struct
2052 {
2053   int filetype;
2054   char *id;
2055 }
2056 filetype_id_list[] =
2057 {
2058   { LEVEL_FILE_TYPE_RND,        "RND"   },
2059   { LEVEL_FILE_TYPE_BD,         "BD"    },
2060   { LEVEL_FILE_TYPE_EM,         "EM"    },
2061   { LEVEL_FILE_TYPE_SP,         "SP"    },
2062   { LEVEL_FILE_TYPE_DX,         "DX"    },
2063   { LEVEL_FILE_TYPE_SB,         "SB"    },
2064   { LEVEL_FILE_TYPE_DC,         "DC"    },
2065   { LEVEL_FILE_TYPE_MM,         "MM"    },
2066   { LEVEL_FILE_TYPE_MM,         "DF"    },
2067   { -1,                         NULL    },
2068 };
2069
2070
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2074
2075 static boolean check_special_flags(char *flag)
2076 {
2077   if (strEqual(options.special_flags, flag) ||
2078       strEqual(leveldir_current->special_flags, flag))
2079     return TRUE;
2080
2081   return FALSE;
2082 }
2083
2084 static struct DateInfo getCurrentDate(void)
2085 {
2086   time_t epoch_seconds = time(NULL);
2087   struct tm *now = localtime(&epoch_seconds);
2088   struct DateInfo date;
2089
2090   date.year  = now->tm_year + 1900;
2091   date.month = now->tm_mon  + 1;
2092   date.day   = now->tm_mday;
2093
2094   date.src   = DATE_SRC_CLOCK;
2095
2096   return date;
2097 }
2098
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2100 {
2101   int i;
2102
2103   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104     change->has_event[i] = FALSE;
2105 }
2106
2107 static void resetEventBits(void)
2108 {
2109   int i;
2110
2111   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112     xx_event_bits[i] = 0;
2113 }
2114
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2116 {
2117   int i;
2118
2119   /* important: only change event flag if corresponding event bit is set
2120      (this is because all xx_event_bits[] values are loaded separately,
2121      and all xx_event_bits[] values are set back to zero before loading
2122      another value xx_event_bits[x] (each value representing 32 flags)) */
2123
2124   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126       change->has_event[i] = TRUE;
2127 }
2128
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2130 {
2131   int i;
2132
2133   /* in contrast to the above function setEventFlagsFromEventBits(), it
2134      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135      depending on the corresponding change->has_event[i] values here, as
2136      all xx_event_bits[] values are reset in resetEventBits() before */
2137
2138   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139     if (change->has_event[i])
2140       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2141 }
2142
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2144 {
2145   static char description[MAX_ELEMENT_NAME_LEN + 1];
2146   char *default_description = (ei->custom_description != NULL ?
2147                                ei->custom_description :
2148                                ei->editor_description);
2149   int i;
2150
2151   // always start with reliable default values
2152   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153     description[i] = '\0';
2154
2155   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2157
2158   return &description[0];
2159 }
2160
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2162 {
2163   char *default_description = getDefaultElementDescription(ei);
2164   int i;
2165
2166   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167     ei->description[i] = default_description[i];
2168 }
2169
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2171 {
2172   int i;
2173
2174   for (i = 0; conf[i].data_type != -1; i++)
2175   {
2176     int default_value = conf[i].default_value;
2177     int data_type = conf[i].data_type;
2178     int conf_type = conf[i].conf_type;
2179     int byte_mask = conf_type & CONF_MASK_BYTES;
2180
2181     if (byte_mask == CONF_MASK_MULTI_BYTES)
2182     {
2183       int default_num_entities = conf[i].default_num_entities;
2184       int max_num_entities = conf[i].max_num_entities;
2185
2186       *(int *)(conf[i].num_entities) = default_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *default_string = conf[i].default_string;
2191         char *string = (char *)(conf[i].value);
2192
2193         strncpy(string, default_string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array = (int *)(conf[i].value);
2198         int j;
2199
2200         for (j = 0; j < max_num_entities; j++)
2201           element_array[j] = default_value;
2202       }
2203       else if (data_type == TYPE_CONTENT_LIST)
2204       {
2205         struct Content *content = (struct Content *)(conf[i].value);
2206         int c, x, y;
2207
2208         for (c = 0; c < max_num_entities; c++)
2209           for (y = 0; y < 3; y++)
2210             for (x = 0; x < 3; x++)
2211               content[c].e[x][y] = default_value;
2212       }
2213     }
2214     else        // constant size configuration data (1, 2 or 4 bytes)
2215     {
2216       if (data_type == TYPE_BOOLEAN)
2217         *(boolean *)(conf[i].value) = default_value;
2218       else
2219         *(int *)    (conf[i].value) = default_value;
2220     }
2221   }
2222 }
2223
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2225 {
2226   int i;
2227
2228   for (i = 0; conf[i].data_type != -1; i++)
2229   {
2230     int data_type = conf[i].data_type;
2231     int conf_type = conf[i].conf_type;
2232     int byte_mask = conf_type & CONF_MASK_BYTES;
2233
2234     if (byte_mask == CONF_MASK_MULTI_BYTES)
2235     {
2236       int max_num_entities = conf[i].max_num_entities;
2237
2238       if (data_type == TYPE_STRING)
2239       {
2240         char *string      = (char *)(conf[i].value);
2241         char *string_copy = (char *)(conf[i].value_copy);
2242
2243         strncpy(string_copy, string, max_num_entities);
2244       }
2245       else if (data_type == TYPE_ELEMENT_LIST)
2246       {
2247         int *element_array      = (int *)(conf[i].value);
2248         int *element_array_copy = (int *)(conf[i].value_copy);
2249         int j;
2250
2251         for (j = 0; j < max_num_entities; j++)
2252           element_array_copy[j] = element_array[j];
2253       }
2254       else if (data_type == TYPE_CONTENT_LIST)
2255       {
2256         struct Content *content      = (struct Content *)(conf[i].value);
2257         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2258         int c, x, y;
2259
2260         for (c = 0; c < max_num_entities; c++)
2261           for (y = 0; y < 3; y++)
2262             for (x = 0; x < 3; x++)
2263               content_copy[c].e[x][y] = content[c].e[x][y];
2264       }
2265     }
2266     else        // constant size configuration data (1, 2 or 4 bytes)
2267     {
2268       if (data_type == TYPE_BOOLEAN)
2269         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2270       else
2271         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2272     }
2273   }
2274 }
2275
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2277 {
2278   int i;
2279
2280   xx_ei = *ei_from;     // copy element data into temporary buffer
2281   yy_ei = *ei_to;       // copy element data into temporary buffer
2282
2283   copyConfigFromConfigList(chunk_config_CUSX_base);
2284
2285   *ei_from = xx_ei;
2286   *ei_to   = yy_ei;
2287
2288   // ---------- reinitialize and copy change pages ----------
2289
2290   ei_to->num_change_pages = ei_from->num_change_pages;
2291   ei_to->current_change_page = ei_from->current_change_page;
2292
2293   setElementChangePages(ei_to, ei_to->num_change_pages);
2294
2295   for (i = 0; i < ei_to->num_change_pages; i++)
2296     ei_to->change_page[i] = ei_from->change_page[i];
2297
2298   // ---------- copy group element info ----------
2299   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2300     *ei_to->group = *ei_from->group;
2301
2302   // mark this custom element as modified
2303   ei_to->modified_settings = TRUE;
2304 }
2305
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2307 {
2308   int change_page_size = sizeof(struct ElementChangeInfo);
2309
2310   ei->num_change_pages = MAX(1, change_pages);
2311
2312   ei->change_page =
2313     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2314
2315   if (ei->current_change_page >= ei->num_change_pages)
2316     ei->current_change_page = ei->num_change_pages - 1;
2317
2318   ei->change = &ei->change_page[ei->current_change_page];
2319 }
2320
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2322 {
2323   xx_change = *change;          // copy change data into temporary buffer
2324
2325   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2326
2327   *change = xx_change;
2328
2329   resetEventFlags(change);
2330
2331   change->direct_action = 0;
2332   change->other_action = 0;
2333
2334   change->pre_change_function = NULL;
2335   change->change_function = NULL;
2336   change->post_change_function = NULL;
2337 }
2338
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2340 {
2341   boolean add_border = FALSE;
2342   int x1 = 0;
2343   int y1 = 0;
2344   int x2 = STD_LEV_FIELDX - 1;
2345   int y2 = STD_LEV_FIELDY - 1;
2346   int i, x, y;
2347
2348   li = *level;          // copy level data into temporary buffer
2349   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350   *level = li;          // copy temporary buffer back to level data
2351
2352   setLevelInfoToDefaults_BD();
2353   setLevelInfoToDefaults_EM();
2354   setLevelInfoToDefaults_SP();
2355   setLevelInfoToDefaults_MM();
2356
2357   level->native_bd_level = &native_bd_level;
2358   level->native_em_level = &native_em_level;
2359   level->native_sp_level = &native_sp_level;
2360   level->native_mm_level = &native_mm_level;
2361
2362   level->file_version = FILE_VERSION_ACTUAL;
2363   level->game_version = GAME_VERSION_ACTUAL;
2364
2365   level->creation_date = getCurrentDate();
2366
2367   level->encoding_16bit_field  = TRUE;
2368   level->encoding_16bit_yamyam = TRUE;
2369   level->encoding_16bit_amoeba = TRUE;
2370
2371   // clear level name and level author string buffers
2372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373     level->name[i] = '\0';
2374   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375     level->author[i] = '\0';
2376
2377   // set level name and level author to default values
2378   strcpy(level->name, NAMELESS_LEVEL_NAME);
2379   strcpy(level->author, ANONYMOUS_NAME);
2380
2381   // set default game engine type
2382   level->game_engine_type = setup.default_game_engine_type;
2383
2384   // some game engines should have a default playfield with border elements
2385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2388   {
2389     add_border = TRUE;
2390     x1++;
2391     y1++;
2392     x2--;
2393     y2--;
2394   }
2395
2396   // set level playfield to playable default level with player and exit
2397   for (x = 0; x < MAX_LEV_FIELDX; x++)
2398   {
2399     for (y = 0; y < MAX_LEV_FIELDY; y++)
2400     {
2401       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402                          y == 0 || y == STD_LEV_FIELDY - 1))
2403         level->field[x][y] = getEngineElement(EL_STEELWALL);
2404       else
2405         level->field[x][y] = getEngineElement(EL_SAND);
2406     }
2407   }
2408
2409   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2411
2412   BorderElement = getEngineElement(EL_STEELWALL);
2413
2414   // detect custom elements when loading them
2415   level->file_has_custom_elements = FALSE;
2416
2417   // set random colors for BD style levels according to preferred color type
2418   SetRandomLevelColors_BD(setup.bd_default_color_type);
2419
2420   // set default color type and colors for BD style level colors
2421   SetDefaultLevelColorType_BD();
2422   SetDefaultLevelColors_BD();
2423
2424   // set all bug compatibility flags to "false" => do not emulate this bug
2425   level->use_action_after_change_bug = FALSE;
2426
2427   if (leveldir_current)
2428   {
2429     // try to determine better author name than 'anonymous'
2430     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2431     {
2432       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2434     }
2435     else
2436     {
2437       switch (LEVELCLASS(leveldir_current))
2438       {
2439         case LEVELCLASS_TUTORIAL:
2440           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2441           break;
2442
2443         case LEVELCLASS_CONTRIB:
2444           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2446           break;
2447
2448         case LEVELCLASS_PRIVATE:
2449           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2451           break;
2452
2453         default:
2454           // keep default value
2455           break;
2456       }
2457     }
2458   }
2459 }
2460
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2462 {
2463   static boolean clipboard_elements_initialized = FALSE;
2464   int i;
2465
2466   InitElementPropertiesStatic();
2467
2468   li = *level;          // copy level data into temporary buffer
2469   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470   *level = li;          // copy temporary buffer back to level data
2471
2472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473   {
2474     int element = i;
2475     struct ElementInfo *ei = &element_info[element];
2476
2477     if (element == EL_MM_GRAY_BALL)
2478     {
2479       struct LevelInfo_MM *level_mm = level->native_mm_level;
2480       int j;
2481
2482       for (j = 0; j < level->num_mm_ball_contents; j++)
2483         level->mm_ball_content[j] =
2484           map_element_MM_to_RND(level_mm->ball_content[j]);
2485     }
2486
2487     // never initialize clipboard elements after the very first time
2488     // (to be able to use clipboard elements between several levels)
2489     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2490       continue;
2491
2492     if (IS_ENVELOPE(element))
2493     {
2494       int envelope_nr = element - EL_ENVELOPE_1;
2495
2496       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2497
2498       level->envelope[envelope_nr] = xx_envelope;
2499     }
2500
2501     if (IS_CUSTOM_ELEMENT(element) ||
2502         IS_GROUP_ELEMENT(element) ||
2503         IS_INTERNAL_ELEMENT(element))
2504     {
2505       xx_ei = *ei;      // copy element data into temporary buffer
2506
2507       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508
2509       *ei = xx_ei;
2510     }
2511
2512     setElementChangePages(ei, 1);
2513     setElementChangeInfoToDefaults(ei->change);
2514
2515     if (IS_CUSTOM_ELEMENT(element) ||
2516         IS_GROUP_ELEMENT(element))
2517     {
2518       setElementDescriptionToDefault(ei);
2519
2520       ei->modified_settings = FALSE;
2521     }
2522
2523     if (IS_CUSTOM_ELEMENT(element) ||
2524         IS_INTERNAL_ELEMENT(element))
2525     {
2526       // internal values used in level editor
2527
2528       ei->access_type = 0;
2529       ei->access_layer = 0;
2530       ei->access_protected = 0;
2531       ei->walk_to_action = 0;
2532       ei->smash_targets = 0;
2533       ei->deadliness = 0;
2534
2535       ei->can_explode_by_fire = FALSE;
2536       ei->can_explode_smashed = FALSE;
2537       ei->can_explode_impact = FALSE;
2538
2539       ei->current_change_page = 0;
2540     }
2541
2542     if (IS_GROUP_ELEMENT(element) ||
2543         IS_INTERNAL_ELEMENT(element))
2544     {
2545       struct ElementGroupInfo *group;
2546
2547       // initialize memory for list of elements in group
2548       if (ei->group == NULL)
2549         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2550
2551       group = ei->group;
2552
2553       xx_group = *group;        // copy group data into temporary buffer
2554
2555       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556
2557       *group = xx_group;
2558     }
2559
2560     if (IS_EMPTY_ELEMENT(element) ||
2561         IS_INTERNAL_ELEMENT(element))
2562     {
2563       xx_ei = *ei;              // copy element data into temporary buffer
2564
2565       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2566
2567       *ei = xx_ei;
2568     }
2569   }
2570
2571   clipboard_elements_initialized = TRUE;
2572 }
2573
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575                                    boolean level_info_only,
2576                                    boolean reset_file_status)
2577 {
2578   setLevelInfoToDefaults_Level(level);
2579
2580   if (!level_info_only)
2581     setLevelInfoToDefaults_Elements(level);
2582
2583   if (reset_file_status)
2584   {
2585     level->no_valid_file = FALSE;
2586     level->no_level_file = FALSE;
2587   }
2588
2589   level->changed = FALSE;
2590 }
2591
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2593 {
2594   level_file_info->nr = 0;
2595   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596   level_file_info->packed = FALSE;
2597
2598   setString(&level_file_info->basename, NULL);
2599   setString(&level_file_info->filename, NULL);
2600 }
2601
2602 int getMappedElement_SB(int, boolean);
2603
2604 static void ActivateLevelTemplate(void)
2605 {
2606   int x, y;
2607
2608   if (check_special_flags("load_xsb_to_ces"))
2609   {
2610     // fill smaller playfields with padding "beyond border wall" elements
2611     if (level.fieldx < level_template.fieldx ||
2612         level.fieldy < level_template.fieldy)
2613     {
2614       short field[level.fieldx][level.fieldy];
2615       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2619
2620       // copy old playfield (which is smaller than the visible area)
2621       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622         field[x][y] = level.field[x][y];
2623
2624       // fill new, larger playfield with "beyond border wall" elements
2625       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626         level.field[x][y] = getMappedElement_SB('_', TRUE);
2627
2628       // copy the old playfield to the middle of the new playfield
2629       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2631
2632       level.fieldx = new_fieldx;
2633       level.fieldy = new_fieldy;
2634     }
2635   }
2636
2637   // Currently there is no special action needed to activate the template
2638   // data, because 'element_info' property settings overwrite the original
2639   // level data, while all other variables do not change.
2640
2641   // Exception: 'from_level_template' elements in the original level playfield
2642   // are overwritten with the corresponding elements at the same position in
2643   // playfield from the level template.
2644
2645   for (x = 0; x < level.fieldx; x++)
2646     for (y = 0; y < level.fieldy; y++)
2647       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648         level.field[x][y] = level_template.field[x][y];
2649
2650   if (check_special_flags("load_xsb_to_ces"))
2651   {
2652     struct LevelInfo level_backup = level;
2653
2654     // overwrite all individual level settings from template level settings
2655     level = level_template;
2656
2657     // restore level file info
2658     level.file_info = level_backup.file_info;
2659
2660     // restore playfield size
2661     level.fieldx = level_backup.fieldx;
2662     level.fieldy = level_backup.fieldy;
2663
2664     // restore playfield content
2665     for (x = 0; x < level.fieldx; x++)
2666       for (y = 0; y < level.fieldy; y++)
2667         level.field[x][y] = level_backup.field[x][y];
2668
2669     // restore name and author from individual level
2670     strcpy(level.name,   level_backup.name);
2671     strcpy(level.author, level_backup.author);
2672
2673     // restore flag "use_custom_template"
2674     level.use_custom_template = level_backup.use_custom_template;
2675   }
2676 }
2677
2678 boolean isLevelsetFilename_BD(char *filename)
2679 {
2680   return (strSuffixLower(filename, ".bd") ||
2681           strSuffixLower(filename, ".bdr") ||
2682           strSuffixLower(filename, ".brc") ||
2683           strSuffixLower(filename, ".gds"));
2684 }
2685
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2687 {
2688   // check for native BD level file extensions
2689   if (!isLevelsetFilename_BD(basename))
2690     return FALSE;
2691
2692   // check for standard single-level BD files (like "001.bd")
2693   if (strSuffixLower(basename, ".bd") &&
2694       strlen(basename) == 6 &&
2695       basename[0] >= '0' && basename[0] <= '9' &&
2696       basename[1] >= '0' && basename[1] <= '9' &&
2697       basename[2] >= '0' && basename[2] <= '9')
2698     return FALSE;
2699
2700   // this is a level package in native BD file format
2701   return TRUE;
2702 }
2703
2704 static char *getLevelFilenameFromBasename(char *basename)
2705 {
2706   static char *filename = NULL;
2707
2708   checked_free(filename);
2709
2710   filename = getPath2(getCurrentLevelDir(), basename);
2711
2712   return filename;
2713 }
2714
2715 static int getFileTypeFromBasename(char *basename)
2716 {
2717   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2718
2719   static char *filename = NULL;
2720   struct stat file_status;
2721
2722   // ---------- try to determine file type from filename ----------
2723
2724   // check for typical filename of a Supaplex level package file
2725   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726     return LEVEL_FILE_TYPE_SP;
2727
2728   // check for typical filename of a Diamond Caves II level package file
2729   if (strSuffixLower(basename, ".dc") ||
2730       strSuffixLower(basename, ".dc2"))
2731     return LEVEL_FILE_TYPE_DC;
2732
2733   // check for typical filename of a Sokoban level package file
2734   if (strSuffixLower(basename, ".xsb") &&
2735       strchr(basename, '%') == NULL)
2736     return LEVEL_FILE_TYPE_SB;
2737
2738   // check for typical filename of a Boulder Dash (GDash) level package file
2739   if (checkForPackageFromBasename_BD(basename))
2740     return LEVEL_FILE_TYPE_BD;
2741
2742   // ---------- try to determine file type from filesize ----------
2743
2744   checked_free(filename);
2745   filename = getPath2(getCurrentLevelDir(), basename);
2746
2747   if (stat(filename, &file_status) == 0)
2748   {
2749     // check for typical filesize of a Supaplex level package file
2750     if (file_status.st_size == 170496)
2751       return LEVEL_FILE_TYPE_SP;
2752   }
2753
2754   return LEVEL_FILE_TYPE_UNKNOWN;
2755 }
2756
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2758 {
2759   File *file;
2760
2761   if ((file = openFile(filename, MODE_READ)))
2762   {
2763     char chunk_name[CHUNK_ID_LEN + 1];
2764
2765     getFileChunkBE(file, chunk_name, NULL);
2766
2767     if (strEqual(chunk_name, "MMII") ||
2768         strEqual(chunk_name, "MIRR"))
2769       type = LEVEL_FILE_TYPE_MM;
2770
2771     closeFile(file);
2772   }
2773
2774   return type;
2775 }
2776
2777 static boolean checkForPackageFromBasename(char *basename)
2778 {
2779   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2781
2782   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2783 }
2784
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788
2789   if (nr < 0)
2790     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2791   else
2792     sprintf(basename, "%03d.%s", nr, extension);
2793
2794   return basename;
2795 }
2796
2797 static char *getSingleLevelBasename(int nr)
2798 {
2799   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2800 }
2801
2802 static char *getPackedLevelBasename(int type)
2803 {
2804   static char basename[MAX_FILENAME_LEN];
2805   char *directory = getCurrentLevelDir();
2806   Directory *dir;
2807   DirectoryEntry *dir_entry;
2808
2809   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2810
2811   if ((dir = openDirectory(directory)) == NULL)
2812   {
2813     Warn("cannot read current level directory '%s'", directory);
2814
2815     return basename;
2816   }
2817
2818   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2819   {
2820     char *entry_basename = dir_entry->basename;
2821     int entry_type = getFileTypeFromBasename(entry_basename);
2822
2823     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2824     {
2825       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2826           type == entry_type)
2827       {
2828         strcpy(basename, entry_basename);
2829
2830         break;
2831       }
2832     }
2833   }
2834
2835   closeDirectory(dir);
2836
2837   return basename;
2838 }
2839
2840 static char *getSingleLevelFilename(int nr)
2841 {
2842   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2843 }
2844
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2847 {
2848   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2849 }
2850 #endif
2851
2852 char *getDefaultLevelFilename(int nr)
2853 {
2854   return getSingleLevelFilename(nr);
2855 }
2856
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2859                                                  int type)
2860 {
2861   lfi->type = type;
2862   lfi->packed = FALSE;
2863
2864   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2866 }
2867 #endif
2868
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870                                                  int type, char *format, ...)
2871 {
2872   static char basename[MAX_FILENAME_LEN];
2873   va_list ap;
2874
2875   va_start(ap, format);
2876   vsprintf(basename, format, ap);
2877   va_end(ap);
2878
2879   lfi->type = type;
2880   lfi->packed = FALSE;
2881
2882   setString(&lfi->basename, basename);
2883   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2884 }
2885
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887                                                  int type)
2888 {
2889   lfi->type = type;
2890   lfi->packed = TRUE;
2891
2892   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2894 }
2895
2896 static int getFiletypeFromID(char *filetype_id)
2897 {
2898   char *filetype_id_lower;
2899   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2900   int i;
2901
2902   if (filetype_id == NULL)
2903     return LEVEL_FILE_TYPE_UNKNOWN;
2904
2905   filetype_id_lower = getStringToLower(filetype_id);
2906
2907   for (i = 0; filetype_id_list[i].id != NULL; i++)
2908   {
2909     char *id_lower = getStringToLower(filetype_id_list[i].id);
2910     
2911     if (strEqual(filetype_id_lower, id_lower))
2912       filetype = filetype_id_list[i].filetype;
2913
2914     free(id_lower);
2915
2916     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2917       break;
2918   }
2919
2920   free(filetype_id_lower);
2921
2922   return filetype;
2923 }
2924
2925 char *getLocalLevelTemplateFilename(void)
2926 {
2927   return getDefaultLevelFilename(-1);
2928 }
2929
2930 char *getGlobalLevelTemplateFilename(void)
2931 {
2932   // global variable "leveldir_current" must be modified in the loop below
2933   LevelDirTree *leveldir_current_last = leveldir_current;
2934   char *filename = NULL;
2935
2936   // check for template level in path from current to topmost tree node
2937
2938   while (leveldir_current != NULL)
2939   {
2940     filename = getDefaultLevelFilename(-1);
2941
2942     if (fileExists(filename))
2943       break;
2944
2945     leveldir_current = leveldir_current->node_parent;
2946   }
2947
2948   // restore global variable "leveldir_current" modified in above loop
2949   leveldir_current = leveldir_current_last;
2950
2951   return filename;
2952 }
2953
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2955 {
2956   int nr = lfi->nr;
2957
2958   // special case: level number is negative => check for level template file
2959   if (nr < 0)
2960   {
2961     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962                                          getSingleLevelBasename(-1));
2963
2964     // replace local level template filename with global template filename
2965     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2966
2967     // no fallback if template file not existing
2968     return;
2969   }
2970
2971   // special case: check for file name/pattern specified in "levelinfo.conf"
2972   if (leveldir_current->level_filename != NULL)
2973   {
2974     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2975
2976     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977                                          leveldir_current->level_filename, nr);
2978
2979     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2980
2981     if (fileExists(lfi->filename))
2982       return;
2983   }
2984   else if (leveldir_current->level_filetype != NULL)
2985   {
2986     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2987
2988     // check for specified native level file with standard file name
2989     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2991     if (fileExists(lfi->filename))
2992       return;
2993   }
2994
2995   // check for native Rocks'n'Diamonds level file
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2998   if (fileExists(lfi->filename))
2999     return;
3000
3001   // check for native Boulder Dash level file
3002   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003   if (fileExists(lfi->filename))
3004     return;
3005
3006   // check for Emerald Mine level file (V1)
3007   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008                                        'a' + (nr / 10) % 26, '0' + nr % 10);
3009   if (fileExists(lfi->filename))
3010     return;
3011   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012                                        'A' + (nr / 10) % 26, '0' + nr % 10);
3013   if (fileExists(lfi->filename))
3014     return;
3015
3016   // check for Emerald Mine level file (V2 to V5)
3017   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018   if (fileExists(lfi->filename))
3019     return;
3020
3021   // check for Emerald Mine level file (V6 / single mode)
3022   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023   if (fileExists(lfi->filename))
3024     return;
3025   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026   if (fileExists(lfi->filename))
3027     return;
3028
3029   // check for Emerald Mine level file (V6 / teamwork mode)
3030   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031   if (fileExists(lfi->filename))
3032     return;
3033   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034   if (fileExists(lfi->filename))
3035     return;
3036
3037   // check for various packed level file formats
3038   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039   if (fileExists(lfi->filename))
3040     return;
3041
3042   // no known level file found -- use default values (and fail later)
3043   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3045 }
3046
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3048 {
3049   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050     lfi->type = getFileTypeFromBasename(lfi->basename);
3051
3052   if (lfi->type == LEVEL_FILE_TYPE_RND)
3053     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3054 }
3055
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3057 {
3058   // always start with reliable default values
3059   setFileInfoToDefaults(level_file_info);
3060
3061   level_file_info->nr = nr;     // set requested level number
3062
3063   determineLevelFileInfo_Filename(level_file_info);
3064   determineLevelFileInfo_Filetype(level_file_info);
3065 }
3066
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068                               struct LevelFileInfo *lfi_to)
3069 {
3070   lfi_to->nr     = lfi_from->nr;
3071   lfi_to->type   = lfi_from->type;
3072   lfi_to->packed = lfi_from->packed;
3073
3074   setString(&lfi_to->basename, lfi_from->basename);
3075   setString(&lfi_to->filename, lfi_from->filename);
3076 }
3077
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3081
3082 int getMappedElement(int element)
3083 {
3084   // remap some (historic, now obsolete) elements
3085
3086   switch (element)
3087   {
3088     case EL_PLAYER_OBSOLETE:
3089       element = EL_PLAYER_1;
3090       break;
3091
3092     case EL_KEY_OBSOLETE:
3093       element = EL_KEY_1;
3094       break;
3095
3096     case EL_EM_KEY_1_FILE_OBSOLETE:
3097       element = EL_EM_KEY_1;
3098       break;
3099
3100     case EL_EM_KEY_2_FILE_OBSOLETE:
3101       element = EL_EM_KEY_2;
3102       break;
3103
3104     case EL_EM_KEY_3_FILE_OBSOLETE:
3105       element = EL_EM_KEY_3;
3106       break;
3107
3108     case EL_EM_KEY_4_FILE_OBSOLETE:
3109       element = EL_EM_KEY_4;
3110       break;
3111
3112     case EL_ENVELOPE_OBSOLETE:
3113       element = EL_ENVELOPE_1;
3114       break;
3115
3116     case EL_SP_EMPTY:
3117       element = EL_EMPTY;
3118       break;
3119
3120     default:
3121       if (element >= NUM_FILE_ELEMENTS)
3122       {
3123         Warn("invalid level element %d", element);
3124
3125         element = EL_UNKNOWN;
3126       }
3127       break;
3128   }
3129
3130   return element;
3131 }
3132
3133 static int getMappedElementByVersion(int element, int game_version)
3134 {
3135   // remap some elements due to certain game version
3136
3137   if (game_version <= VERSION_IDENT(2,2,0,0))
3138   {
3139     // map game font elements
3140     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3141                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3143                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3144   }
3145
3146   if (game_version < VERSION_IDENT(3,0,0,0))
3147   {
3148     // map Supaplex gravity tube elements
3149     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3150                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3152                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3153                element);
3154   }
3155
3156   return element;
3157 }
3158
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3160 {
3161   level->file_version = getFileVersion(file);
3162   level->game_version = getFileVersion(file);
3163
3164   return chunk_size;
3165 }
3166
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3168 {
3169   level->creation_date.year  = getFile16BitBE(file);
3170   level->creation_date.month = getFile8Bit(file);
3171   level->creation_date.day   = getFile8Bit(file);
3172
3173   level->creation_date.src   = DATE_SRC_LEVELFILE;
3174
3175   return chunk_size;
3176 }
3177
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3179 {
3180   int initial_player_stepsize;
3181   int initial_player_gravity;
3182   int i, x, y;
3183
3184   level->fieldx = getFile8Bit(file);
3185   level->fieldy = getFile8Bit(file);
3186
3187   level->time           = getFile16BitBE(file);
3188   level->gems_needed    = getFile16BitBE(file);
3189
3190   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191     level->name[i] = getFile8Bit(file);
3192   level->name[MAX_LEVEL_NAME_LEN] = 0;
3193
3194   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195     level->score[i] = getFile8Bit(file);
3196
3197   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199     for (y = 0; y < 3; y++)
3200       for (x = 0; x < 3; x++)
3201         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3202
3203   level->amoeba_speed           = getFile8Bit(file);
3204   level->time_magic_wall        = getFile8Bit(file);
3205   level->time_wheel             = getFile8Bit(file);
3206   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3207
3208   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3209                                    STEPSIZE_NORMAL);
3210
3211   for (i = 0; i < MAX_PLAYERS; i++)
3212     level->initial_player_stepsize[i] = initial_player_stepsize;
3213
3214   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215
3216   for (i = 0; i < MAX_PLAYERS; i++)
3217     level->initial_player_gravity[i] = initial_player_gravity;
3218
3219   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221
3222   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223
3224   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226   level->can_move_into_acid_bits = getFile32BitBE(file);
3227   level->dont_collide_with_bits = getFile8Bit(file);
3228
3229   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3231
3232   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3235
3236   level->game_engine_type       = getFile8Bit(file);
3237
3238   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3239
3240   return chunk_size;
3241 }
3242
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3244 {
3245   int i;
3246
3247   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248     level->name[i] = getFile8Bit(file);
3249   level->name[MAX_LEVEL_NAME_LEN] = 0;
3250
3251   return chunk_size;
3252 }
3253
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3255 {
3256   int i;
3257
3258   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259     level->author[i] = getFile8Bit(file);
3260   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3261
3262   return chunk_size;
3263 }
3264
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3266 {
3267   int x, y;
3268   int chunk_size_expected = level->fieldx * level->fieldy;
3269
3270   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271      stored with 16-bit encoding (and should be twice as big then).
3272      Even worse, playfield data was stored 16-bit when only yamyam content
3273      contained 16-bit elements and vice versa. */
3274
3275   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276     chunk_size_expected *= 2;
3277
3278   if (chunk_size_expected != chunk_size)
3279   {
3280     ReadUnusedBytesFromFile(file, chunk_size);
3281     return chunk_size_expected;
3282   }
3283
3284   for (y = 0; y < level->fieldy; y++)
3285     for (x = 0; x < level->fieldx; x++)
3286       level->field[x][y] =
3287         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3288                          getFile8Bit(file));
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i, x, y;
3295   int header_size = 4;
3296   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297   int chunk_size_expected = header_size + content_size;
3298
3299   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300      stored with 16-bit encoding (and should be twice as big then).
3301      Even worse, playfield data was stored 16-bit when only yamyam content
3302      contained 16-bit elements and vice versa. */
3303
3304   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305     chunk_size_expected += content_size;
3306
3307   if (chunk_size_expected != chunk_size)
3308   {
3309     ReadUnusedBytesFromFile(file, chunk_size);
3310     return chunk_size_expected;
3311   }
3312
3313   getFile8Bit(file);
3314   level->num_yamyam_contents = getFile8Bit(file);
3315   getFile8Bit(file);
3316   getFile8Bit(file);
3317
3318   // correct invalid number of content fields -- should never happen
3319   if (level->num_yamyam_contents < 1 ||
3320       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3322
3323   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324     for (y = 0; y < 3; y++)
3325       for (x = 0; x < 3; x++)
3326         level->yamyam_content[i].e[x][y] =
3327           getMappedElement(level->encoding_16bit_field ?
3328                            getFile16BitBE(file) : getFile8Bit(file));
3329   return chunk_size;
3330 }
3331
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3333 {
3334   int i, x, y;
3335   int element;
3336   int num_contents;
3337   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3338
3339   element = getMappedElement(getFile16BitBE(file));
3340   num_contents = getFile8Bit(file);
3341
3342   getFile8Bit(file);    // content x size (unused)
3343   getFile8Bit(file);    // content y size (unused)
3344
3345   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3346
3347   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348     for (y = 0; y < 3; y++)
3349       for (x = 0; x < 3; x++)
3350         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3351
3352   // correct invalid number of content fields -- should never happen
3353   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354     num_contents = STD_ELEMENT_CONTENTS;
3355
3356   if (element == EL_YAMYAM)
3357   {
3358     level->num_yamyam_contents = num_contents;
3359
3360     for (i = 0; i < num_contents; i++)
3361       for (y = 0; y < 3; y++)
3362         for (x = 0; x < 3; x++)
3363           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3364   }
3365   else if (element == EL_BD_AMOEBA)
3366   {
3367     level->amoeba_content = content_array[0][0][0];
3368   }
3369   else
3370   {
3371     Warn("cannot load content for element '%d'", element);
3372   }
3373
3374   return chunk_size;
3375 }
3376
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3378 {
3379   int i;
3380   int element;
3381   int envelope_nr;
3382   int envelope_len;
3383   int chunk_size_expected;
3384
3385   element = getMappedElement(getFile16BitBE(file));
3386   if (!IS_ENVELOPE(element))
3387     element = EL_ENVELOPE_1;
3388
3389   envelope_nr = element - EL_ENVELOPE_1;
3390
3391   envelope_len = getFile16BitBE(file);
3392
3393   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3395
3396   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3397
3398   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399   if (chunk_size_expected != chunk_size)
3400   {
3401     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402     return chunk_size_expected;
3403   }
3404
3405   for (i = 0; i < envelope_len; i++)
3406     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3407
3408   return chunk_size;
3409 }
3410
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3412 {
3413   int num_changed_custom_elements = getFile16BitBE(file);
3414   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3415   int i;
3416
3417   if (chunk_size_expected != chunk_size)
3418   {
3419     ReadUnusedBytesFromFile(file, chunk_size - 2);
3420     return chunk_size_expected;
3421   }
3422
3423   for (i = 0; i < num_changed_custom_elements; i++)
3424   {
3425     int element = getMappedElement(getFile16BitBE(file));
3426     int properties = getFile32BitBE(file);
3427
3428     if (IS_CUSTOM_ELEMENT(element))
3429       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3430     else
3431       Warn("invalid custom element number %d", element);
3432
3433     // older game versions that wrote level files with CUS1 chunks used
3434     // different default push delay values (not yet stored in level file)
3435     element_info[element].push_delay_fixed = 2;
3436     element_info[element].push_delay_random = 8;
3437   }
3438
3439   level->file_has_custom_elements = TRUE;
3440
3441   return chunk_size;
3442 }
3443
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3445 {
3446   int num_changed_custom_elements = getFile16BitBE(file);
3447   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3448   int i;
3449
3450   if (chunk_size_expected != chunk_size)
3451   {
3452     ReadUnusedBytesFromFile(file, chunk_size - 2);
3453     return chunk_size_expected;
3454   }
3455
3456   for (i = 0; i < num_changed_custom_elements; i++)
3457   {
3458     int element = getMappedElement(getFile16BitBE(file));
3459     int custom_target_element = getMappedElement(getFile16BitBE(file));
3460
3461     if (IS_CUSTOM_ELEMENT(element))
3462       element_info[element].change->target_element = custom_target_element;
3463     else
3464       Warn("invalid custom element number %d", element);
3465   }
3466
3467   level->file_has_custom_elements = TRUE;
3468
3469   return chunk_size;
3470 }
3471
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3473 {
3474   int num_changed_custom_elements = getFile16BitBE(file);
3475   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3476   int i, j, x, y;
3477
3478   if (chunk_size_expected != chunk_size)
3479   {
3480     ReadUnusedBytesFromFile(file, chunk_size - 2);
3481     return chunk_size_expected;
3482   }
3483
3484   for (i = 0; i < num_changed_custom_elements; i++)
3485   {
3486     int element = getMappedElement(getFile16BitBE(file));
3487     struct ElementInfo *ei = &element_info[element];
3488     unsigned int event_bits;
3489
3490     if (!IS_CUSTOM_ELEMENT(element))
3491     {
3492       Warn("invalid custom element number %d", element);
3493
3494       element = EL_INTERNAL_DUMMY;
3495     }
3496
3497     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498       ei->description[j] = getFile8Bit(file);
3499     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3500
3501     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3502
3503     // some free bytes for future properties and padding
3504     ReadUnusedBytesFromFile(file, 7);
3505
3506     ei->use_gfx_element = getFile8Bit(file);
3507     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3508
3509     ei->collect_score_initial = getFile8Bit(file);
3510     ei->collect_count_initial = getFile8Bit(file);
3511
3512     ei->push_delay_fixed = getFile16BitBE(file);
3513     ei->push_delay_random = getFile16BitBE(file);
3514     ei->move_delay_fixed = getFile16BitBE(file);
3515     ei->move_delay_random = getFile16BitBE(file);
3516
3517     ei->move_pattern = getFile16BitBE(file);
3518     ei->move_direction_initial = getFile8Bit(file);
3519     ei->move_stepsize = getFile8Bit(file);
3520
3521     for (y = 0; y < 3; y++)
3522       for (x = 0; x < 3; x++)
3523         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3524
3525     // bits 0 - 31 of "has_event[]"
3526     event_bits = getFile32BitBE(file);
3527     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528       if (event_bits & (1u << j))
3529         ei->change->has_event[j] = TRUE;
3530
3531     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3532
3533     ei->change->delay_fixed = getFile16BitBE(file);
3534     ei->change->delay_random = getFile16BitBE(file);
3535     ei->change->delay_frames = getFile16BitBE(file);
3536
3537     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3538
3539     ei->change->explode = getFile8Bit(file);
3540     ei->change->use_target_content = getFile8Bit(file);
3541     ei->change->only_if_complete = getFile8Bit(file);
3542     ei->change->use_random_replace = getFile8Bit(file);
3543
3544     ei->change->random_percentage = getFile8Bit(file);
3545     ei->change->replace_when = getFile8Bit(file);
3546
3547     for (y = 0; y < 3; y++)
3548       for (x = 0; x < 3; x++)
3549         ei->change->target_content.e[x][y] =
3550           getMappedElement(getFile16BitBE(file));
3551
3552     ei->slippery_type = getFile8Bit(file);
3553
3554     // some free bytes for future properties and padding
3555     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3556
3557     // mark that this custom element has been modified
3558     ei->modified_settings = TRUE;
3559   }
3560
3561   level->file_has_custom_elements = TRUE;
3562
3563   return chunk_size;
3564 }
3565
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3567 {
3568   struct ElementInfo *ei;
3569   int chunk_size_expected;
3570   int element;
3571   int i, j, x, y;
3572
3573   // ---------- custom element base property values (96 bytes) ----------------
3574
3575   element = getMappedElement(getFile16BitBE(file));
3576
3577   if (!IS_CUSTOM_ELEMENT(element))
3578   {
3579     Warn("invalid custom element number %d", element);
3580
3581     ReadUnusedBytesFromFile(file, chunk_size - 2);
3582
3583     return chunk_size;
3584   }
3585
3586   ei = &element_info[element];
3587
3588   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589     ei->description[i] = getFile8Bit(file);
3590   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3591
3592   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3593
3594   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3595
3596   ei->num_change_pages = getFile8Bit(file);
3597
3598   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599   if (chunk_size_expected != chunk_size)
3600   {
3601     ReadUnusedBytesFromFile(file, chunk_size - 43);
3602     return chunk_size_expected;
3603   }
3604
3605   ei->ce_value_fixed_initial = getFile16BitBE(file);
3606   ei->ce_value_random_initial = getFile16BitBE(file);
3607   ei->use_last_ce_value = getFile8Bit(file);
3608
3609   ei->use_gfx_element = getFile8Bit(file);
3610   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3611
3612   ei->collect_score_initial = getFile8Bit(file);
3613   ei->collect_count_initial = getFile8Bit(file);
3614
3615   ei->drop_delay_fixed = getFile8Bit(file);
3616   ei->push_delay_fixed = getFile8Bit(file);
3617   ei->drop_delay_random = getFile8Bit(file);
3618   ei->push_delay_random = getFile8Bit(file);
3619   ei->move_delay_fixed = getFile16BitBE(file);
3620   ei->move_delay_random = getFile16BitBE(file);
3621
3622   // bits 0 - 15 of "move_pattern" ...
3623   ei->move_pattern = getFile16BitBE(file);
3624   ei->move_direction_initial = getFile8Bit(file);
3625   ei->move_stepsize = getFile8Bit(file);
3626
3627   ei->slippery_type = getFile8Bit(file);
3628
3629   for (y = 0; y < 3; y++)
3630     for (x = 0; x < 3; x++)
3631       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3632
3633   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635   ei->move_leave_type = getFile8Bit(file);
3636
3637   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638   ei->move_pattern |= (getFile16BitBE(file) << 16);
3639
3640   ei->access_direction = getFile8Bit(file);
3641
3642   ei->explosion_delay = getFile8Bit(file);
3643   ei->ignition_delay = getFile8Bit(file);
3644   ei->explosion_type = getFile8Bit(file);
3645
3646   // some free bytes for future custom property values and padding
3647   ReadUnusedBytesFromFile(file, 1);
3648
3649   // ---------- change page property values (48 bytes) ------------------------
3650
3651   setElementChangePages(ei, ei->num_change_pages);
3652
3653   for (i = 0; i < ei->num_change_pages; i++)
3654   {
3655     struct ElementChangeInfo *change = &ei->change_page[i];
3656     unsigned int event_bits;
3657
3658     // always start with reliable default values
3659     setElementChangeInfoToDefaults(change);
3660
3661     // bits 0 - 31 of "has_event[]" ...
3662     event_bits = getFile32BitBE(file);
3663     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664       if (event_bits & (1u << j))
3665         change->has_event[j] = TRUE;
3666
3667     change->target_element = getMappedElement(getFile16BitBE(file));
3668
3669     change->delay_fixed = getFile16BitBE(file);
3670     change->delay_random = getFile16BitBE(file);
3671     change->delay_frames = getFile16BitBE(file);
3672
3673     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3674
3675     change->explode = getFile8Bit(file);
3676     change->use_target_content = getFile8Bit(file);
3677     change->only_if_complete = getFile8Bit(file);
3678     change->use_random_replace = getFile8Bit(file);
3679
3680     change->random_percentage = getFile8Bit(file);
3681     change->replace_when = getFile8Bit(file);
3682
3683     for (y = 0; y < 3; y++)
3684       for (x = 0; x < 3; x++)
3685         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3686
3687     change->can_change = getFile8Bit(file);
3688
3689     change->trigger_side = getFile8Bit(file);
3690
3691     change->trigger_player = getFile8Bit(file);
3692     change->trigger_page = getFile8Bit(file);
3693
3694     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695                             CH_PAGE_ANY : (1 << change->trigger_page));
3696
3697     change->has_action = getFile8Bit(file);
3698     change->action_type = getFile8Bit(file);
3699     change->action_mode = getFile8Bit(file);
3700     change->action_arg = getFile16BitBE(file);
3701
3702     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703     event_bits = getFile8Bit(file);
3704     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705       if (event_bits & (1u << (j - 32)))
3706         change->has_event[j] = TRUE;
3707   }
3708
3709   // mark this custom element as modified
3710   ei->modified_settings = TRUE;
3711
3712   level->file_has_custom_elements = TRUE;
3713
3714   return chunk_size;
3715 }
3716
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3718 {
3719   struct ElementInfo *ei;
3720   struct ElementGroupInfo *group;
3721   int element;
3722   int i;
3723
3724   element = getMappedElement(getFile16BitBE(file));
3725
3726   if (!IS_GROUP_ELEMENT(element))
3727   {
3728     Warn("invalid group element number %d", element);
3729
3730     ReadUnusedBytesFromFile(file, chunk_size - 2);
3731
3732     return chunk_size;
3733   }
3734
3735   ei = &element_info[element];
3736
3737   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738     ei->description[i] = getFile8Bit(file);
3739   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3740
3741   group = element_info[element].group;
3742
3743   group->num_elements = getFile8Bit(file);
3744
3745   ei->use_gfx_element = getFile8Bit(file);
3746   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3747
3748   group->choice_mode = getFile8Bit(file);
3749
3750   // some free bytes for future values and padding
3751   ReadUnusedBytesFromFile(file, 3);
3752
3753   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754     group->element[i] = getMappedElement(getFile16BitBE(file));
3755
3756   // mark this group element as modified
3757   element_info[element].modified_settings = TRUE;
3758
3759   level->file_has_custom_elements = TRUE;
3760
3761   return chunk_size;
3762 }
3763
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765                                 int element, int real_element)
3766 {
3767   int micro_chunk_size = 0;
3768   int conf_type = getFile8Bit(file);
3769   int byte_mask = conf_type & CONF_MASK_BYTES;
3770   boolean element_found = FALSE;
3771   int i;
3772
3773   micro_chunk_size += 1;
3774
3775   if (byte_mask == CONF_MASK_MULTI_BYTES)
3776   {
3777     int num_bytes = getFile16BitBE(file);
3778     byte *buffer = checked_malloc(num_bytes);
3779
3780     ReadBytesFromFile(file, buffer, num_bytes);
3781
3782     for (i = 0; conf[i].data_type != -1; i++)
3783     {
3784       if (conf[i].element == element &&
3785           conf[i].conf_type == conf_type)
3786       {
3787         int data_type = conf[i].data_type;
3788         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789         int max_num_entities = conf[i].max_num_entities;
3790
3791         if (num_entities > max_num_entities)
3792         {
3793           Warn("truncating number of entities for element %d from %d to %d",
3794                element, num_entities, max_num_entities);
3795
3796           num_entities = max_num_entities;
3797         }
3798
3799         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800                                   data_type == TYPE_CONTENT_LIST))
3801         {
3802           // for element and content lists, zero entities are not allowed
3803           Warn("found empty list of entities for element %d", element);
3804
3805           // do not set "num_entities" here to prevent reading behind buffer
3806
3807           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3808         }
3809         else
3810         {
3811           *(int *)(conf[i].num_entities) = num_entities;
3812         }
3813
3814         element_found = TRUE;
3815
3816         if (data_type == TYPE_STRING)
3817         {
3818           char *string = (char *)(conf[i].value);
3819           int j;
3820
3821           for (j = 0; j < max_num_entities; j++)
3822             string[j] = (j < num_entities ? buffer[j] : '\0');
3823         }
3824         else if (data_type == TYPE_ELEMENT_LIST)
3825         {
3826           int *element_array = (int *)(conf[i].value);
3827           int j;
3828
3829           for (j = 0; j < num_entities; j++)
3830             element_array[j] =
3831               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3832         }
3833         else if (data_type == TYPE_CONTENT_LIST)
3834         {
3835           struct Content *content= (struct Content *)(conf[i].value);
3836           int c, x, y;
3837
3838           for (c = 0; c < num_entities; c++)
3839             for (y = 0; y < 3; y++)
3840               for (x = 0; x < 3; x++)
3841                 content[c].e[x][y] =
3842                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3843         }
3844         else
3845           element_found = FALSE;
3846
3847         break;
3848       }
3849     }
3850
3851     checked_free(buffer);
3852
3853     micro_chunk_size += 2 + num_bytes;
3854   }
3855   else          // constant size configuration data (1, 2 or 4 bytes)
3856   {
3857     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3858                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3860
3861     for (i = 0; conf[i].data_type != -1; i++)
3862     {
3863       if (conf[i].element == element &&
3864           conf[i].conf_type == conf_type)
3865       {
3866         int data_type = conf[i].data_type;
3867
3868         if (data_type == TYPE_ELEMENT)
3869           value = getMappedElement(value);
3870
3871         if (data_type == TYPE_BOOLEAN)
3872           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3873         else
3874           *(int *)    (conf[i].value) = value;
3875
3876         element_found = TRUE;
3877
3878         break;
3879       }
3880     }
3881
3882     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3883   }
3884
3885   if (!element_found)
3886   {
3887     char *error_conf_chunk_bytes =
3888       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892     int error_element = real_element;
3893
3894     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895          error_conf_chunk_bytes, error_conf_chunk_token,
3896          error_element, EL_NAME(error_element));
3897   }
3898
3899   return micro_chunk_size;
3900 }
3901
3902 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int real_chunk_size = 0;
3905
3906   li = *level;          // copy level data into temporary buffer
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3911
3912     if (real_chunk_size >= chunk_size)
3913       break;
3914   }
3915
3916   *level = li;          // copy temporary buffer back to level data
3917
3918   return real_chunk_size;
3919 }
3920
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3922 {
3923   int real_chunk_size = 0;
3924
3925   li = *level;          // copy level data into temporary buffer
3926
3927   while (!checkEndOfFile(file))
3928   {
3929     int element = getMappedElement(getFile16BitBE(file));
3930
3931     real_chunk_size += 2;
3932     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3933                                             element, element);
3934     if (real_chunk_size >= chunk_size)
3935       break;
3936   }
3937
3938   *level = li;          // copy temporary buffer back to level data
3939
3940   return real_chunk_size;
3941 }
3942
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3944 {
3945   int real_chunk_size = 0;
3946
3947   li = *level;          // copy level data into temporary buffer
3948
3949   while (!checkEndOfFile(file))
3950   {
3951     int element = getMappedElement(getFile16BitBE(file));
3952
3953     real_chunk_size += 2;
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3955                                             element, element);
3956     if (real_chunk_size >= chunk_size)
3957       break;
3958   }
3959
3960   *level = li;          // copy temporary buffer back to level data
3961
3962   return real_chunk_size;
3963 }
3964
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3966 {
3967   int element = getMappedElement(getFile16BitBE(file));
3968   int envelope_nr = element - EL_ENVELOPE_1;
3969   int real_chunk_size = 2;
3970
3971   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3972
3973   while (!checkEndOfFile(file))
3974   {
3975     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3976                                             -1, element);
3977
3978     if (real_chunk_size >= chunk_size)
3979       break;
3980   }
3981
3982   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3983
3984   return real_chunk_size;
3985 }
3986
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3988 {
3989   int element = getMappedElement(getFile16BitBE(file));
3990   int real_chunk_size = 2;
3991   struct ElementInfo *ei = &element_info[element];
3992   int i;
3993
3994   xx_ei = *ei;          // copy element data into temporary buffer
3995
3996   xx_ei.num_change_pages = -1;
3997
3998   while (!checkEndOfFile(file))
3999   {
4000     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4001                                             -1, element);
4002     if (xx_ei.num_change_pages != -1)
4003       break;
4004
4005     if (real_chunk_size >= chunk_size)
4006       break;
4007   }
4008
4009   *ei = xx_ei;
4010
4011   if (ei->num_change_pages == -1)
4012   {
4013     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4014          EL_NAME(element));
4015
4016     ei->num_change_pages = 1;
4017
4018     setElementChangePages(ei, 1);
4019     setElementChangeInfoToDefaults(ei->change);
4020
4021     return real_chunk_size;
4022   }
4023
4024   // initialize number of change pages stored for this custom element
4025   setElementChangePages(ei, ei->num_change_pages);
4026   for (i = 0; i < ei->num_change_pages; i++)
4027     setElementChangeInfoToDefaults(&ei->change_page[i]);
4028
4029   // start with reading properties for the first change page
4030   xx_current_change_page = 0;
4031
4032   while (!checkEndOfFile(file))
4033   {
4034     // level file might contain invalid change page number
4035     if (xx_current_change_page >= ei->num_change_pages)
4036       break;
4037
4038     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4039
4040     xx_change = *change;        // copy change data into temporary buffer
4041
4042     resetEventBits();           // reset bits; change page might have changed
4043
4044     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4045                                             -1, element);
4046
4047     *change = xx_change;
4048
4049     setEventFlagsFromEventBits(change);
4050
4051     if (real_chunk_size >= chunk_size)
4052       break;
4053   }
4054
4055   level->file_has_custom_elements = TRUE;
4056
4057   return real_chunk_size;
4058 }
4059
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4061 {
4062   int element = getMappedElement(getFile16BitBE(file));
4063   int real_chunk_size = 2;
4064   struct ElementInfo *ei = &element_info[element];
4065   struct ElementGroupInfo *group = ei->group;
4066
4067   if (group == NULL)
4068     return -1;
4069
4070   xx_ei = *ei;          // copy element data into temporary buffer
4071   xx_group = *group;    // copy group data into temporary buffer
4072
4073   while (!checkEndOfFile(file))
4074   {
4075     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4076                                             -1, element);
4077
4078     if (real_chunk_size >= chunk_size)
4079       break;
4080   }
4081
4082   *ei = xx_ei;
4083   *group = xx_group;
4084
4085   level->file_has_custom_elements = TRUE;
4086
4087   return real_chunk_size;
4088 }
4089
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4091 {
4092   int element = getMappedElement(getFile16BitBE(file));
4093   int real_chunk_size = 2;
4094   struct ElementInfo *ei = &element_info[element];
4095
4096   xx_ei = *ei;          // copy element data into temporary buffer
4097
4098   while (!checkEndOfFile(file))
4099   {
4100     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4101                                             -1, element);
4102
4103     if (real_chunk_size >= chunk_size)
4104       break;
4105   }
4106
4107   *ei = xx_ei;
4108
4109   level->file_has_custom_elements = TRUE;
4110
4111   return real_chunk_size;
4112 }
4113
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115                                       struct LevelFileInfo *level_file_info,
4116                                       boolean level_info_only)
4117 {
4118   char *filename = level_file_info->filename;
4119   char cookie[MAX_LINE_LEN];
4120   char chunk_name[CHUNK_ID_LEN + 1];
4121   int chunk_size;
4122   File *file;
4123
4124   if (!(file = openFile(filename, MODE_READ)))
4125   {
4126     level->no_valid_file = TRUE;
4127     level->no_level_file = TRUE;
4128
4129     if (level_info_only)
4130       return;
4131
4132     Warn("cannot read level '%s' -- using empty level", filename);
4133
4134     if (!setup.editor.use_template_for_new_levels)
4135       return;
4136
4137     // if level file not found, try to initialize level data from template
4138     filename = getGlobalLevelTemplateFilename();
4139
4140     if (!(file = openFile(filename, MODE_READ)))
4141       return;
4142
4143     // default: for empty levels, use level template for custom elements
4144     level->use_custom_template = TRUE;
4145
4146     level->no_valid_file = FALSE;
4147   }
4148
4149   getFileChunkBE(file, chunk_name, NULL);
4150   if (strEqual(chunk_name, "RND1"))
4151   {
4152     getFile32BitBE(file);               // not used
4153
4154     getFileChunkBE(file, chunk_name, NULL);
4155     if (!strEqual(chunk_name, "CAVE"))
4156     {
4157       level->no_valid_file = TRUE;
4158
4159       Warn("unknown format of level file '%s'", filename);
4160
4161       closeFile(file);
4162
4163       return;
4164     }
4165   }
4166   else  // check for pre-2.0 file format with cookie string
4167   {
4168     strcpy(cookie, chunk_name);
4169     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4170       cookie[4] = '\0';
4171     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172       cookie[strlen(cookie) - 1] = '\0';
4173
4174     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4175     {
4176       level->no_valid_file = TRUE;
4177
4178       Warn("unknown format of level file '%s'", filename);
4179
4180       closeFile(file);
4181
4182       return;
4183     }
4184
4185     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4186     {
4187       level->no_valid_file = TRUE;
4188
4189       Warn("unsupported version of level file '%s'", filename);
4190
4191       closeFile(file);
4192
4193       return;
4194     }
4195
4196     // pre-2.0 level files have no game version, so use file version here
4197     level->game_version = level->file_version;
4198   }
4199
4200   if (level->file_version < FILE_VERSION_1_2)
4201   {
4202     // level files from versions before 1.2.0 without chunk structure
4203     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4204     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4205   }
4206   else
4207   {
4208     static struct
4209     {
4210       char *name;
4211       int size;
4212       int (*loader)(File *, int, struct LevelInfo *);
4213     }
4214     chunk_info[] =
4215     {
4216       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4217       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4218       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4219       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4220       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4221       { "INFO", -1,                     LoadLevel_INFO },
4222       { "BODY", -1,                     LoadLevel_BODY },
4223       { "CONT", -1,                     LoadLevel_CONT },
4224       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4225       { "CNT3", -1,                     LoadLevel_CNT3 },
4226       { "CUS1", -1,                     LoadLevel_CUS1 },
4227       { "CUS2", -1,                     LoadLevel_CUS2 },
4228       { "CUS3", -1,                     LoadLevel_CUS3 },
4229       { "CUS4", -1,                     LoadLevel_CUS4 },
4230       { "GRP1", -1,                     LoadLevel_GRP1 },
4231       { "CONF", -1,                     LoadLevel_CONF },
4232       { "ELEM", -1,                     LoadLevel_ELEM },
4233       { "NOTE", -1,                     LoadLevel_NOTE },
4234       { "CUSX", -1,                     LoadLevel_CUSX },
4235       { "GRPX", -1,                     LoadLevel_GRPX },
4236       { "EMPX", -1,                     LoadLevel_EMPX },
4237
4238       {  NULL,  0,                      NULL }
4239     };
4240
4241     while (getFileChunkBE(file, chunk_name, &chunk_size))
4242     {
4243       int i = 0;
4244
4245       while (chunk_info[i].name != NULL &&
4246              !strEqual(chunk_name, chunk_info[i].name))
4247         i++;
4248
4249       if (chunk_info[i].name == NULL)
4250       {
4251         Warn("unknown chunk '%s' in level file '%s'",
4252              chunk_name, filename);
4253
4254         ReadUnusedBytesFromFile(file, chunk_size);
4255       }
4256       else if (chunk_info[i].size != -1 &&
4257                chunk_info[i].size != chunk_size)
4258       {
4259         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260              chunk_size, chunk_name, filename);
4261
4262         ReadUnusedBytesFromFile(file, chunk_size);
4263       }
4264       else
4265       {
4266         // call function to load this level chunk
4267         int chunk_size_expected =
4268           (chunk_info[i].loader)(file, chunk_size, level);
4269
4270         if (chunk_size_expected < 0)
4271         {
4272           Warn("error reading chunk '%s' in level file '%s'",
4273                chunk_name, filename);
4274
4275           break;
4276         }
4277
4278         // the size of some chunks cannot be checked before reading other
4279         // chunks first (like "HEAD" and "BODY") that contain some header
4280         // information, so check them here
4281         if (chunk_size_expected != chunk_size)
4282         {
4283           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284                chunk_size, chunk_name, filename);
4285
4286           break;
4287         }
4288       }
4289     }
4290   }
4291
4292   closeFile(file);
4293 }
4294
4295
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4299
4300 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4302
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4304 {
4305   struct LevelInfo_BD *level_bd = level->native_bd_level;
4306   GdCave *cave = NULL;  // will be changed below
4307   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4309   int x, y;
4310
4311   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4312
4313   // cave and map newly allocated when set to defaults above
4314   cave = level_bd->cave;
4315
4316   // level type
4317   cave->intermission                    = level->bd_intermission;
4318
4319   // level settings
4320   cave->level_time[0]                   = level->time;
4321   cave->level_diamonds[0]               = level->gems_needed;
4322
4323   // game timing
4324   cave->scheduling                      = level->bd_scheduling_type;
4325   cave->pal_timing                      = level->bd_pal_timing;
4326   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4327   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4328   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4329   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4330
4331   // scores
4332   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4333   cave->diamond_value                   = level->score[SC_EMERALD];
4334   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4335
4336   // compatibility settings
4337   cave->lineshift                       = level->bd_line_shifting_borders;
4338   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4339   cave->short_explosions                = level->bd_short_explosions;
4340
4341   // player properties
4342   cave->diagonal_movements              = level->bd_diagonal_movements;
4343   cave->active_is_first_found           = level->bd_topmost_player_active;
4344   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4345   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4346   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4348
4349   // element properties
4350   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4351   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4352   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4353   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4354   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4355   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4356   cave->level_magic_wall_time[0]        = level->bd_magic_wall_time;
4357   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4358   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4359   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4360   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4361
4362   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4369
4370   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4371   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4373   cave->level_amoeba_threshold[0]       = level->bd_amoeba_1_threshold_too_big;
4374   cave->level_amoeba_time[0]            = level->bd_amoeba_1_slow_growth_time;
4375   cave->amoeba_growth_prob              = level->bd_amoeba_1_slow_growth_rate * 10000;
4376   cave->amoeba_fast_growth_prob         = level->bd_amoeba_1_fast_growth_rate * 10000;
4377   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4378   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4379   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4380   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4381
4382   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4388
4389   cave->slime_predictable               = level->bd_slime_is_predictable;
4390   cave->slime_correct_random            = level->bd_slime_correct_random;
4391   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4392   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4394   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4395   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4401
4402   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4404   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4405
4406   cave->biter_delay_frame               = level->bd_biter_move_delay;
4407   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4408
4409   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4410
4411   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4412
4413   cave->replicators_active              = level->bd_replicators_active;
4414   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4415
4416   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4417   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4418
4419   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4420
4421   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4422
4423   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4424   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4425   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4426
4427   cave->infinite_rockets                = level->bd_infinite_rockets;
4428
4429   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4430   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4431
4432   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4434
4435   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4436   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4438
4439   cave->gravity                         = level->bd_gravity_direction;
4440   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4441   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4442   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4443
4444   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4448
4449   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4455
4456   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4460
4461   cave->colorb                          = level->bd_color_b;
4462   cave->color0                          = level->bd_color_0;
4463   cave->color1                          = level->bd_color_1;
4464   cave->color2                          = level->bd_color_2;
4465   cave->color3                          = level->bd_color_3;
4466   cave->color4                          = level->bd_color_4;
4467   cave->color5                          = level->bd_color_5;
4468
4469   // level name
4470   strncpy(cave->name, level->name, sizeof(GdString));
4471   cave->name[sizeof(GdString) - 1] = '\0';
4472
4473   // playfield elements
4474   for (x = 0; x < cave->w; x++)
4475     for (y = 0; y < cave->h; y++)
4476       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4477 }
4478
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4480 {
4481   struct LevelInfo_BD *level_bd = level->native_bd_level;
4482   GdCave *cave = level_bd->cave;
4483   int bd_level_nr = level_bd->level_nr;
4484   int x, y;
4485
4486   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4488
4489   // level type
4490   level->bd_intermission                = cave->intermission;
4491
4492   // level settings
4493   level->time                           = cave->level_time[bd_level_nr];
4494   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4495
4496   // game timing
4497   level->bd_scheduling_type             = cave->scheduling;
4498   level->bd_pal_timing                  = cave->pal_timing;
4499   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4500   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4501   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4502   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4503
4504   // scores
4505   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4506   level->score[SC_EMERALD]              = cave->diamond_value;
4507   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4508
4509   // compatibility settings
4510   level->bd_line_shifting_borders       = cave->lineshift;
4511   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4512   level->bd_short_explosions            = cave->short_explosions;
4513
4514   // player properties
4515   level->bd_diagonal_movements          = cave->diagonal_movements;
4516   level->bd_topmost_player_active       = cave->active_is_first_found;
4517   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4518   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4519   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4520   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4521
4522   // element properties
4523   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4524   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4525   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4526   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4527   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4528   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4529   level->bd_magic_wall_time             = cave->level_magic_wall_time[bd_level_nr];
4530   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4531   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4532   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4533   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4534
4535   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4537   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4539   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4542
4543   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4544   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4545   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4546   level->bd_amoeba_1_threshold_too_big  = cave->level_amoeba_threshold[bd_level_nr];
4547   level->bd_amoeba_1_slow_growth_time   = cave->level_amoeba_time[bd_level_nr];
4548   level->bd_amoeba_1_slow_growth_rate   = cave->amoeba_growth_prob      / 10000;
4549   level->bd_amoeba_1_fast_growth_rate   = cave->amoeba_fast_growth_prob / 10000;
4550   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4551   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4552   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4553   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4554
4555   level->bd_amoeba_1_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556   level->bd_amoeba_1_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4561
4562   level->bd_slime_is_predictable        = cave->slime_predictable;
4563   level->bd_slime_correct_random        = cave->slime_correct_random;
4564   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4565   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4567   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4568   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4569   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4571   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4573   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4574
4575   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4576   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4577   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4578
4579   level->bd_biter_move_delay            = cave->biter_delay_frame;
4580   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4581
4582   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4583
4584   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4585
4586   level->bd_replicators_active          = cave->replicators_active;
4587   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4588
4589   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4590   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4591
4592   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4593
4594   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4595
4596   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4597   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4598   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4599
4600   level->bd_infinite_rockets            = cave->infinite_rockets;
4601
4602   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4604
4605   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4607
4608   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4609   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4610   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4611
4612   level->bd_gravity_direction           = cave->gravity;
4613   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4614   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4615   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4616
4617   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4621
4622   level->bd_firefly_1_explodes_to       = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624   level->bd_butterfly_1_explodes_to     = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4628
4629   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4633
4634   level->bd_color_b                     = cave->colorb;
4635   level->bd_color_0                     = cave->color0;
4636   level->bd_color_1                     = cave->color1;
4637   level->bd_color_2                     = cave->color2;
4638   level->bd_color_3                     = cave->color3;
4639   level->bd_color_4                     = cave->color4;
4640   level->bd_color_5                     = cave->color5;
4641
4642   // set default color type and colors for BD style level colors
4643   SetDefaultLevelColorType_BD();
4644   SetDefaultLevelColors_BD();
4645
4646   // level name
4647   char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4648   char *cave_name_final = (gd_caveset_has_levels() ?
4649                            getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4650                            getStringCopy(cave_name_latin1));
4651
4652   strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4653   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4654
4655   // playfield elements
4656   for (x = 0; x < level->fieldx; x++)
4657     for (y = 0; y < level->fieldy; y++)
4658       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4659
4660   checked_free(cave_name_latin1);
4661   checked_free(cave_name_final);
4662 }
4663
4664 static void setTapeInfoToDefaults(void);
4665
4666 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4667 {
4668   struct LevelInfo_BD *level_bd = level->native_bd_level;
4669   GdCave *cave = level_bd->cave;
4670   GdReplay *replay = level_bd->replay;
4671   int i;
4672
4673   if (replay == NULL)
4674     return;
4675
4676   // always start with reliable default values
4677   setTapeInfoToDefaults();
4678
4679   tape.level_nr = level_nr;             // (currently not used)
4680   tape.random_seed = replay->seed;
4681
4682   TapeSetDateFromIsoDateString(replay->date);
4683
4684   tape.counter = 0;
4685   tape.pos[tape.counter].delay = 0;
4686
4687   tape.bd_replay = TRUE;
4688
4689   // all time calculations only used to display approximate tape time
4690   int cave_speed = cave->speed;
4691   int milliseconds_game = 0;
4692   int milliseconds_elapsed = 20;
4693
4694   for (i = 0; i < replay->movements->len; i++)
4695   {
4696     int replay_action = replay->movements->data[i];
4697     int tape_action = map_action_BD_to_RND(replay_action);
4698     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4699     boolean success = 0;
4700
4701     while (1)
4702     {
4703       success = TapeAddAction(action);
4704
4705       milliseconds_game += milliseconds_elapsed;
4706
4707       if (milliseconds_game >= cave_speed)
4708       {
4709         milliseconds_game -= cave_speed;
4710
4711         break;
4712       }
4713     }
4714
4715     tape.counter++;
4716     tape.pos[tape.counter].delay = 0;
4717     tape.pos[tape.counter].action[0] = 0;
4718
4719     if (!success)
4720     {
4721       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4722
4723       break;
4724     }
4725   }
4726
4727   TapeHaltRecording();
4728
4729   if (!replay->success)
4730     Warn("BD replay is marked as not successful");
4731 }
4732
4733
4734 // ----------------------------------------------------------------------------
4735 // functions for loading EM level
4736 // ----------------------------------------------------------------------------
4737
4738 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4739 {
4740   static int ball_xy[8][2] =
4741   {
4742     { 0, 0 },
4743     { 1, 0 },
4744     { 2, 0 },
4745     { 0, 1 },
4746     { 2, 1 },
4747     { 0, 2 },
4748     { 1, 2 },
4749     { 2, 2 },
4750   };
4751   struct LevelInfo_EM *level_em = level->native_em_level;
4752   struct CAVE *cav = level_em->cav;
4753   int i, j, x, y;
4754
4755   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4756   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4757
4758   cav->time_seconds     = level->time;
4759   cav->gems_needed      = level->gems_needed;
4760
4761   cav->emerald_score    = level->score[SC_EMERALD];
4762   cav->diamond_score    = level->score[SC_DIAMOND];
4763   cav->alien_score      = level->score[SC_ROBOT];
4764   cav->tank_score       = level->score[SC_SPACESHIP];
4765   cav->bug_score        = level->score[SC_BUG];
4766   cav->eater_score      = level->score[SC_YAMYAM];
4767   cav->nut_score        = level->score[SC_NUT];
4768   cav->dynamite_score   = level->score[SC_DYNAMITE];
4769   cav->key_score        = level->score[SC_KEY];
4770   cav->exit_score       = level->score[SC_TIME_BONUS];
4771
4772   cav->num_eater_arrays = level->num_yamyam_contents;
4773
4774   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4775     for (y = 0; y < 3; y++)
4776       for (x = 0; x < 3; x++)
4777         cav->eater_array[i][y * 3 + x] =
4778           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4779
4780   cav->amoeba_time              = level->amoeba_speed;
4781   cav->wonderwall_time          = level->time_magic_wall;
4782   cav->wheel_time               = level->time_wheel;
4783
4784   cav->android_move_time        = level->android_move_time;
4785   cav->android_clone_time       = level->android_clone_time;
4786   cav->ball_random              = level->ball_random;
4787   cav->ball_active              = level->ball_active_initial;
4788   cav->ball_time                = level->ball_time;
4789   cav->num_ball_arrays          = level->num_ball_contents;
4790
4791   cav->lenses_score             = level->lenses_score;
4792   cav->magnify_score            = level->magnify_score;
4793   cav->slurp_score              = level->slurp_score;
4794
4795   cav->lenses_time              = level->lenses_time;
4796   cav->magnify_time             = level->magnify_time;
4797
4798   cav->wind_time = 9999;
4799   cav->wind_direction =
4800     map_direction_RND_to_EM(level->wind_direction_initial);
4801
4802   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4803     for (j = 0; j < 8; j++)
4804       cav->ball_array[i][j] =
4805         map_element_RND_to_EM_cave(level->ball_content[i].
4806                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4807
4808   map_android_clone_elements_RND_to_EM(level);
4809
4810   // first fill the complete playfield with the empty space element
4811   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4812     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4813       cav->cave[x][y] = Cblank;
4814
4815   // then copy the real level contents from level file into the playfield
4816   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4817   {
4818     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4819
4820     if (level->field[x][y] == EL_AMOEBA_DEAD)
4821       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4822
4823     cav->cave[x][y] = new_element;
4824   }
4825
4826   for (i = 0; i < MAX_PLAYERS; i++)
4827   {
4828     cav->player_x[i] = -1;
4829     cav->player_y[i] = -1;
4830   }
4831
4832   // initialize player positions and delete players from the playfield
4833   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4834   {
4835     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4836     {
4837       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4838
4839       cav->player_x[player_nr] = x;
4840       cav->player_y[player_nr] = y;
4841
4842       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4843     }
4844   }
4845 }
4846
4847 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4848 {
4849   static int ball_xy[8][2] =
4850   {
4851     { 0, 0 },
4852     { 1, 0 },
4853     { 2, 0 },
4854     { 0, 1 },
4855     { 2, 1 },
4856     { 0, 2 },
4857     { 1, 2 },
4858     { 2, 2 },
4859   };
4860   struct LevelInfo_EM *level_em = level->native_em_level;
4861   struct CAVE *cav = level_em->cav;
4862   int i, j, x, y;
4863
4864   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4865   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4866
4867   level->time        = cav->time_seconds;
4868   level->gems_needed = cav->gems_needed;
4869
4870   sprintf(level->name, "Level %d", level->file_info.nr);
4871
4872   level->score[SC_EMERALD]      = cav->emerald_score;
4873   level->score[SC_DIAMOND]      = cav->diamond_score;
4874   level->score[SC_ROBOT]        = cav->alien_score;
4875   level->score[SC_SPACESHIP]    = cav->tank_score;
4876   level->score[SC_BUG]          = cav->bug_score;
4877   level->score[SC_YAMYAM]       = cav->eater_score;
4878   level->score[SC_NUT]          = cav->nut_score;
4879   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4880   level->score[SC_KEY]          = cav->key_score;
4881   level->score[SC_TIME_BONUS]   = cav->exit_score;
4882
4883   level->num_yamyam_contents    = cav->num_eater_arrays;
4884
4885   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4886     for (y = 0; y < 3; y++)
4887       for (x = 0; x < 3; x++)
4888         level->yamyam_content[i].e[x][y] =
4889           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4890
4891   level->amoeba_speed           = cav->amoeba_time;
4892   level->time_magic_wall        = cav->wonderwall_time;
4893   level->time_wheel             = cav->wheel_time;
4894
4895   level->android_move_time      = cav->android_move_time;
4896   level->android_clone_time     = cav->android_clone_time;
4897   level->ball_random            = cav->ball_random;
4898   level->ball_active_initial    = cav->ball_active;
4899   level->ball_time              = cav->ball_time;
4900   level->num_ball_contents      = cav->num_ball_arrays;
4901
4902   level->lenses_score           = cav->lenses_score;
4903   level->magnify_score          = cav->magnify_score;
4904   level->slurp_score            = cav->slurp_score;
4905
4906   level->lenses_time            = cav->lenses_time;
4907   level->magnify_time           = cav->magnify_time;
4908
4909   level->wind_direction_initial =
4910     map_direction_EM_to_RND(cav->wind_direction);
4911
4912   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4913     for (j = 0; j < 8; j++)
4914       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4915         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4916
4917   map_android_clone_elements_EM_to_RND(level);
4918
4919   // convert the playfield (some elements need special treatment)
4920   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4921   {
4922     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4923
4924     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4925       new_element = EL_AMOEBA_DEAD;
4926
4927     level->field[x][y] = new_element;
4928   }
4929
4930   for (i = 0; i < MAX_PLAYERS; i++)
4931   {
4932     // in case of all players set to the same field, use the first player
4933     int nr = MAX_PLAYERS - i - 1;
4934     int jx = cav->player_x[nr];
4935     int jy = cav->player_y[nr];
4936
4937     if (jx != -1 && jy != -1)
4938       level->field[jx][jy] = EL_PLAYER_1 + nr;
4939   }
4940
4941   // time score is counted for each 10 seconds left in Emerald Mine levels
4942   level->time_score_base = 10;
4943 }
4944
4945
4946 // ----------------------------------------------------------------------------
4947 // functions for loading SP level
4948 // ----------------------------------------------------------------------------
4949
4950 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4951 {
4952   struct LevelInfo_SP *level_sp = level->native_sp_level;
4953   LevelInfoType *header = &level_sp->header;
4954   int i, x, y;
4955
4956   level_sp->width  = level->fieldx;
4957   level_sp->height = level->fieldy;
4958
4959   for (x = 0; x < level->fieldx; x++)
4960     for (y = 0; y < level->fieldy; y++)
4961       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4962
4963   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4964
4965   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4966     header->LevelTitle[i] = level->name[i];
4967   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4968
4969   header->InfotronsNeeded = level->gems_needed;
4970
4971   header->SpecialPortCount = 0;
4972
4973   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4974   {
4975     boolean gravity_port_found = FALSE;
4976     boolean gravity_port_valid = FALSE;
4977     int gravity_port_flag;
4978     int gravity_port_base_element;
4979     int element = level->field[x][y];
4980
4981     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4982         element <= EL_SP_GRAVITY_ON_PORT_UP)
4983     {
4984       gravity_port_found = TRUE;
4985       gravity_port_valid = TRUE;
4986       gravity_port_flag = 1;
4987       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4988     }
4989     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4990              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4991     {
4992       gravity_port_found = TRUE;
4993       gravity_port_valid = TRUE;
4994       gravity_port_flag = 0;
4995       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4996     }
4997     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4998              element <= EL_SP_GRAVITY_PORT_UP)
4999     {
5000       // change R'n'D style gravity inverting special port to normal port
5001       // (there are no gravity inverting ports in native Supaplex engine)
5002
5003       gravity_port_found = TRUE;
5004       gravity_port_valid = FALSE;
5005       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5006     }
5007
5008     if (gravity_port_found)
5009     {
5010       if (gravity_port_valid &&
5011           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5012       {
5013         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5014
5015         port->PortLocation = (y * level->fieldx + x) * 2;
5016         port->Gravity = gravity_port_flag;
5017
5018         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5019
5020         header->SpecialPortCount++;
5021       }
5022       else
5023       {
5024         // change special gravity port to normal port
5025
5026         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5027       }
5028
5029       level_sp->playfield[x][y] = element - EL_SP_START;
5030     }
5031   }
5032 }
5033
5034 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5035 {
5036   struct LevelInfo_SP *level_sp = level->native_sp_level;
5037   LevelInfoType *header = &level_sp->header;
5038   boolean num_invalid_elements = 0;
5039   int i, j, x, y;
5040
5041   level->fieldx = level_sp->width;
5042   level->fieldy = level_sp->height;
5043
5044   for (x = 0; x < level->fieldx; x++)
5045   {
5046     for (y = 0; y < level->fieldy; y++)
5047     {
5048       int element_old = level_sp->playfield[x][y];
5049       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5050
5051       if (element_new == EL_UNKNOWN)
5052       {
5053         num_invalid_elements++;
5054
5055         Debug("level:native:SP", "invalid element %d at position %d, %d",
5056               element_old, x, y);
5057       }
5058
5059       level->field[x][y] = element_new;
5060     }
5061   }
5062
5063   if (num_invalid_elements > 0)
5064     Warn("found %d invalid elements%s", num_invalid_elements,
5065          (!options.debug ? " (use '--debug' for more details)" : ""));
5066
5067   for (i = 0; i < MAX_PLAYERS; i++)
5068     level->initial_player_gravity[i] =
5069       (header->InitialGravity == 1 ? TRUE : FALSE);
5070
5071   // skip leading spaces
5072   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5073     if (header->LevelTitle[i] != ' ')
5074       break;
5075
5076   // copy level title
5077   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5078     level->name[j] = header->LevelTitle[i];
5079   level->name[j] = '\0';
5080
5081   // cut trailing spaces
5082   for (; j > 0; j--)
5083     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5084       level->name[j - 1] = '\0';
5085
5086   level->gems_needed = header->InfotronsNeeded;
5087
5088   for (i = 0; i < header->SpecialPortCount; i++)
5089   {
5090     SpecialPortType *port = &header->SpecialPort[i];
5091     int port_location = port->PortLocation;
5092     int gravity = port->Gravity;
5093     int port_x, port_y, port_element;
5094
5095     port_x = (port_location / 2) % level->fieldx;
5096     port_y = (port_location / 2) / level->fieldx;
5097
5098     if (port_x < 0 || port_x >= level->fieldx ||
5099         port_y < 0 || port_y >= level->fieldy)
5100     {
5101       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5102
5103       continue;
5104     }
5105
5106     port_element = level->field[port_x][port_y];
5107
5108     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5109         port_element > EL_SP_GRAVITY_PORT_UP)
5110     {
5111       Warn("no special port at position (%d, %d)", port_x, port_y);
5112
5113       continue;
5114     }
5115
5116     // change previous (wrong) gravity inverting special port to either
5117     // gravity enabling special port or gravity disabling special port
5118     level->field[port_x][port_y] +=
5119       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5120        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5121   }
5122
5123   // change special gravity ports without database entries to normal ports
5124   for (x = 0; x < level->fieldx; x++)
5125     for (y = 0; y < level->fieldy; y++)
5126       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5127           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5128         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5129
5130   level->time = 0;                      // no time limit
5131   level->amoeba_speed = 0;
5132   level->time_magic_wall = 0;
5133   level->time_wheel = 0;
5134   level->amoeba_content = EL_EMPTY;
5135
5136   // original Supaplex does not use score values -- rate by playing time
5137   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5138     level->score[i] = 0;
5139
5140   level->rate_time_over_score = TRUE;
5141
5142   // there are no yamyams in supaplex levels
5143   for (i = 0; i < level->num_yamyam_contents; i++)
5144     for (x = 0; x < 3; x++)
5145       for (y = 0; y < 3; y++)
5146         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5147 }
5148
5149 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5150 {
5151   struct LevelInfo_SP *level_sp = level->native_sp_level;
5152   struct DemoInfo_SP *demo = &level_sp->demo;
5153   int i, j;
5154
5155   // always start with reliable default values
5156   demo->is_available = FALSE;
5157   demo->length = 0;
5158
5159   if (TAPE_IS_EMPTY(tape))
5160     return;
5161
5162   demo->level_nr = tape.level_nr;       // (currently not used)
5163
5164   level_sp->header.DemoRandomSeed = tape.random_seed;
5165
5166   demo->length = 0;
5167
5168   for (i = 0; i < tape.length; i++)
5169   {
5170     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5171     int demo_repeat = tape.pos[i].delay;
5172     int demo_entries = (demo_repeat + 15) / 16;
5173
5174     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5175     {
5176       Warn("tape truncated: size exceeds maximum SP demo size %d",
5177            SP_MAX_TAPE_LEN);
5178
5179       break;
5180     }
5181
5182     for (j = 0; j < demo_repeat / 16; j++)
5183       demo->data[demo->length++] = 0xf0 | demo_action;
5184
5185     if (demo_repeat % 16)
5186       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5187   }
5188
5189   demo->is_available = TRUE;
5190 }
5191
5192 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5193 {
5194   struct LevelInfo_SP *level_sp = level->native_sp_level;
5195   struct DemoInfo_SP *demo = &level_sp->demo;
5196   char *filename = level->file_info.filename;
5197   int i;
5198
5199   // always start with reliable default values
5200   setTapeInfoToDefaults();
5201
5202   if (!demo->is_available)
5203     return;
5204
5205   tape.level_nr = demo->level_nr;       // (currently not used)
5206   tape.random_seed = level_sp->header.DemoRandomSeed;
5207
5208   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5209
5210   tape.counter = 0;
5211   tape.pos[tape.counter].delay = 0;
5212
5213   for (i = 0; i < demo->length; i++)
5214   {
5215     int demo_action = demo->data[i] & 0x0f;
5216     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5217     int tape_action = map_key_SP_to_RND(demo_action);
5218     int tape_repeat = demo_repeat + 1;
5219     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5220     boolean success = 0;
5221     int j;
5222
5223     for (j = 0; j < tape_repeat; j++)
5224       success = TapeAddAction(action);
5225
5226     if (!success)
5227     {
5228       Warn("SP demo truncated: size exceeds maximum tape size %d",
5229            MAX_TAPE_LEN);
5230
5231       break;
5232     }
5233   }
5234
5235   TapeHaltRecording();
5236 }
5237
5238
5239 // ----------------------------------------------------------------------------
5240 // functions for loading MM level
5241 // ----------------------------------------------------------------------------
5242
5243 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5244 {
5245   struct LevelInfo_MM *level_mm = level->native_mm_level;
5246   int i, x, y;
5247
5248   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5249   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5250
5251   level_mm->time = level->time;
5252   level_mm->kettles_needed = level->gems_needed;
5253   level_mm->auto_count_kettles = level->auto_count_gems;
5254
5255   level_mm->mm_laser_red   = level->mm_laser_red;
5256   level_mm->mm_laser_green = level->mm_laser_green;
5257   level_mm->mm_laser_blue  = level->mm_laser_blue;
5258
5259   level_mm->df_laser_red   = level->df_laser_red;
5260   level_mm->df_laser_green = level->df_laser_green;
5261   level_mm->df_laser_blue  = level->df_laser_blue;
5262
5263   strcpy(level_mm->name, level->name);
5264   strcpy(level_mm->author, level->author);
5265
5266   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5267   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5268   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5269   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5270   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5271
5272   level_mm->amoeba_speed = level->amoeba_speed;
5273   level_mm->time_fuse    = level->mm_time_fuse;
5274   level_mm->time_bomb    = level->mm_time_bomb;
5275   level_mm->time_ball    = level->mm_time_ball;
5276   level_mm->time_block   = level->mm_time_block;
5277
5278   level_mm->num_ball_contents = level->num_mm_ball_contents;
5279   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5280   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5281   level_mm->explode_ball = level->explode_mm_ball;
5282
5283   for (i = 0; i < level->num_mm_ball_contents; i++)
5284     level_mm->ball_content[i] =
5285       map_element_RND_to_MM(level->mm_ball_content[i]);
5286
5287   for (x = 0; x < level->fieldx; x++)
5288     for (y = 0; y < level->fieldy; y++)
5289       Ur[x][y] =
5290         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5291 }
5292
5293 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5294 {
5295   struct LevelInfo_MM *level_mm = level->native_mm_level;
5296   int i, x, y;
5297
5298   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5299   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5300
5301   level->time = level_mm->time;
5302   level->gems_needed = level_mm->kettles_needed;
5303   level->auto_count_gems = level_mm->auto_count_kettles;
5304
5305   level->mm_laser_red   = level_mm->mm_laser_red;
5306   level->mm_laser_green = level_mm->mm_laser_green;
5307   level->mm_laser_blue  = level_mm->mm_laser_blue;
5308
5309   level->df_laser_red   = level_mm->df_laser_red;
5310   level->df_laser_green = level_mm->df_laser_green;
5311   level->df_laser_blue  = level_mm->df_laser_blue;
5312
5313   strcpy(level->name, level_mm->name);
5314
5315   // only overwrite author from 'levelinfo.conf' if author defined in level
5316   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5317     strcpy(level->author, level_mm->author);
5318
5319   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5320   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5321   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5322   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5323   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5324
5325   level->amoeba_speed  = level_mm->amoeba_speed;
5326   level->mm_time_fuse  = level_mm->time_fuse;
5327   level->mm_time_bomb  = level_mm->time_bomb;
5328   level->mm_time_ball  = level_mm->time_ball;
5329   level->mm_time_block = level_mm->time_block;
5330
5331   level->num_mm_ball_contents = level_mm->num_ball_contents;
5332   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5333   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5334   level->explode_mm_ball = level_mm->explode_ball;
5335
5336   for (i = 0; i < level->num_mm_ball_contents; i++)
5337     level->mm_ball_content[i] =
5338       map_element_MM_to_RND(level_mm->ball_content[i]);
5339
5340   for (x = 0; x < level->fieldx; x++)
5341     for (y = 0; y < level->fieldy; y++)
5342       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5343 }
5344
5345
5346 // ----------------------------------------------------------------------------
5347 // functions for loading DC level
5348 // ----------------------------------------------------------------------------
5349
5350 #define DC_LEVEL_HEADER_SIZE            344
5351
5352 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5353                                         boolean init)
5354 {
5355   static int last_data_encoded;
5356   static int offset1;
5357   static int offset2;
5358   int diff;
5359   int diff_hi, diff_lo;
5360   int data_hi, data_lo;
5361   unsigned short data_decoded;
5362
5363   if (init)
5364   {
5365     last_data_encoded = 0;
5366     offset1 = -1;
5367     offset2 = 0;
5368
5369     return 0;
5370   }
5371
5372   diff = data_encoded - last_data_encoded;
5373   diff_hi = diff & ~0xff;
5374   diff_lo = diff &  0xff;
5375
5376   offset2 += diff_lo;
5377
5378   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5379   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5380   data_hi = data_hi & 0xff00;
5381
5382   data_decoded = data_hi | data_lo;
5383
5384   last_data_encoded = data_encoded;
5385
5386   offset1 = (offset1 + 1) % 31;
5387   offset2 = offset2 & 0xff;
5388
5389   return data_decoded;
5390 }
5391
5392 static int getMappedElement_DC(int element)
5393 {
5394   switch (element)
5395   {
5396     case 0x0000:
5397       element = EL_ROCK;
5398       break;
5399
5400       // 0x0117 - 0x036e: (?)
5401       // EL_DIAMOND
5402
5403       // 0x042d - 0x0684: (?)
5404       // EL_EMERALD
5405
5406     case 0x06f1:
5407       element = EL_NUT;
5408       break;
5409
5410     case 0x074c:
5411       element = EL_BOMB;
5412       break;
5413
5414     case 0x07a4:
5415       element = EL_PEARL;
5416       break;
5417
5418     case 0x0823:
5419       element = EL_CRYSTAL;
5420       break;
5421
5422     case 0x0e77:        // quicksand (boulder)
5423       element = EL_QUICKSAND_FAST_FULL;
5424       break;
5425
5426     case 0x0e99:        // slow quicksand (boulder)
5427       element = EL_QUICKSAND_FULL;
5428       break;
5429
5430     case 0x0ed2:
5431       element = EL_EM_EXIT_OPEN;
5432       break;
5433
5434     case 0x0ee3:
5435       element = EL_EM_EXIT_CLOSED;
5436       break;
5437
5438     case 0x0eeb:
5439       element = EL_EM_STEEL_EXIT_OPEN;
5440       break;
5441
5442     case 0x0efc:
5443       element = EL_EM_STEEL_EXIT_CLOSED;
5444       break;
5445
5446     case 0x0f4f:        // dynamite (lit 1)
5447       element = EL_EM_DYNAMITE_ACTIVE;
5448       break;
5449
5450     case 0x0f57:        // dynamite (lit 2)
5451       element = EL_EM_DYNAMITE_ACTIVE;
5452       break;
5453
5454     case 0x0f5f:        // dynamite (lit 3)
5455       element = EL_EM_DYNAMITE_ACTIVE;
5456       break;
5457
5458     case 0x0f67:        // dynamite (lit 4)
5459       element = EL_EM_DYNAMITE_ACTIVE;
5460       break;
5461
5462     case 0x0f81:
5463     case 0x0f82:
5464     case 0x0f83:
5465     case 0x0f84:
5466       element = EL_AMOEBA_WET;
5467       break;
5468
5469     case 0x0f85:
5470       element = EL_AMOEBA_DROP;
5471       break;
5472
5473     case 0x0fb9:
5474       element = EL_DC_MAGIC_WALL;
5475       break;
5476
5477     case 0x0fd0:
5478       element = EL_SPACESHIP_UP;
5479       break;
5480
5481     case 0x0fd9:
5482       element = EL_SPACESHIP_DOWN;
5483       break;
5484
5485     case 0x0ff1:
5486       element = EL_SPACESHIP_LEFT;
5487       break;
5488
5489     case 0x0ff9:
5490       element = EL_SPACESHIP_RIGHT;
5491       break;
5492
5493     case 0x1057:
5494       element = EL_BUG_UP;
5495       break;
5496
5497     case 0x1060:
5498       element = EL_BUG_DOWN;
5499       break;
5500
5501     case 0x1078:
5502       element = EL_BUG_LEFT;
5503       break;
5504
5505     case 0x1080:
5506       element = EL_BUG_RIGHT;
5507       break;
5508
5509     case 0x10de:
5510       element = EL_MOLE_UP;
5511       break;
5512
5513     case 0x10e7:
5514       element = EL_MOLE_DOWN;
5515       break;
5516
5517     case 0x10ff:
5518       element = EL_MOLE_LEFT;
5519       break;
5520
5521     case 0x1107:
5522       element = EL_MOLE_RIGHT;
5523       break;
5524
5525     case 0x11c0:
5526       element = EL_ROBOT;
5527       break;
5528
5529     case 0x13f5:
5530       element = EL_YAMYAM_UP;
5531       break;
5532
5533     case 0x1425:
5534       element = EL_SWITCHGATE_OPEN;
5535       break;
5536
5537     case 0x1426:
5538       element = EL_SWITCHGATE_CLOSED;
5539       break;
5540
5541     case 0x1437:
5542       element = EL_DC_SWITCHGATE_SWITCH_UP;
5543       break;
5544
5545     case 0x143a:
5546       element = EL_TIMEGATE_CLOSED;
5547       break;
5548
5549     case 0x144c:        // conveyor belt switch (green)
5550       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5551       break;
5552
5553     case 0x144f:        // conveyor belt switch (red)
5554       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5555       break;
5556
5557     case 0x1452:        // conveyor belt switch (blue)
5558       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5559       break;
5560
5561     case 0x145b:
5562       element = EL_CONVEYOR_BELT_3_MIDDLE;
5563       break;
5564
5565     case 0x1463:
5566       element = EL_CONVEYOR_BELT_3_LEFT;
5567       break;
5568
5569     case 0x146b:
5570       element = EL_CONVEYOR_BELT_3_RIGHT;
5571       break;
5572
5573     case 0x1473:
5574       element = EL_CONVEYOR_BELT_1_MIDDLE;
5575       break;
5576
5577     case 0x147b:
5578       element = EL_CONVEYOR_BELT_1_LEFT;
5579       break;
5580
5581     case 0x1483:
5582       element = EL_CONVEYOR_BELT_1_RIGHT;
5583       break;
5584
5585     case 0x148b:
5586       element = EL_CONVEYOR_BELT_4_MIDDLE;
5587       break;
5588
5589     case 0x1493:
5590       element = EL_CONVEYOR_BELT_4_LEFT;
5591       break;
5592
5593     case 0x149b:
5594       element = EL_CONVEYOR_BELT_4_RIGHT;
5595       break;
5596
5597     case 0x14ac:
5598       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5599       break;
5600
5601     case 0x14bd:
5602       element = EL_EXPANDABLE_WALL_VERTICAL;
5603       break;
5604
5605     case 0x14c6:
5606       element = EL_EXPANDABLE_WALL_ANY;
5607       break;
5608
5609     case 0x14ce:        // growing steel wall (left/right)
5610       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5611       break;
5612
5613     case 0x14df:        // growing steel wall (up/down)
5614       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5615       break;
5616
5617     case 0x14e8:        // growing steel wall (up/down/left/right)
5618       element = EL_EXPANDABLE_STEELWALL_ANY;
5619       break;
5620
5621     case 0x14e9:
5622       element = EL_SHIELD_DEADLY;
5623       break;
5624
5625     case 0x1501:
5626       element = EL_EXTRA_TIME;
5627       break;
5628
5629     case 0x154f:
5630       element = EL_ACID;
5631       break;
5632
5633     case 0x1577:
5634       element = EL_EMPTY_SPACE;
5635       break;
5636
5637     case 0x1578:        // quicksand (empty)
5638       element = EL_QUICKSAND_FAST_EMPTY;
5639       break;
5640
5641     case 0x1579:        // slow quicksand (empty)
5642       element = EL_QUICKSAND_EMPTY;
5643       break;
5644
5645       // 0x157c - 0x158b:
5646       // EL_SAND
5647
5648       // 0x1590 - 0x159f:
5649       // EL_DC_LANDMINE
5650
5651     case 0x15a0:
5652       element = EL_EM_DYNAMITE;
5653       break;
5654
5655     case 0x15a1:        // key (red)
5656       element = EL_EM_KEY_1;
5657       break;
5658
5659     case 0x15a2:        // key (yellow)
5660       element = EL_EM_KEY_2;
5661       break;
5662
5663     case 0x15a3:        // key (blue)
5664       element = EL_EM_KEY_4;
5665       break;
5666
5667     case 0x15a4:        // key (green)
5668       element = EL_EM_KEY_3;
5669       break;
5670
5671     case 0x15a5:        // key (white)
5672       element = EL_DC_KEY_WHITE;
5673       break;
5674
5675     case 0x15a6:
5676       element = EL_WALL_SLIPPERY;
5677       break;
5678
5679     case 0x15a7:
5680       element = EL_WALL;
5681       break;
5682
5683     case 0x15a8:        // wall (not round)
5684       element = EL_WALL;
5685       break;
5686
5687     case 0x15a9:        // (blue)
5688       element = EL_CHAR_A;
5689       break;
5690
5691     case 0x15aa:        // (blue)
5692       element = EL_CHAR_B;
5693       break;
5694
5695     case 0x15ab:        // (blue)
5696       element = EL_CHAR_C;
5697       break;
5698
5699     case 0x15ac:        // (blue)
5700       element = EL_CHAR_D;
5701       break;
5702
5703     case 0x15ad:        // (blue)
5704       element = EL_CHAR_E;
5705       break;
5706
5707     case 0x15ae:        // (blue)
5708       element = EL_CHAR_F;
5709       break;
5710
5711     case 0x15af:        // (blue)
5712       element = EL_CHAR_G;
5713       break;
5714
5715     case 0x15b0:        // (blue)
5716       element = EL_CHAR_H;
5717       break;
5718
5719     case 0x15b1:        // (blue)
5720       element = EL_CHAR_I;
5721       break;
5722
5723     case 0x15b2:        // (blue)
5724       element = EL_CHAR_J;
5725       break;
5726
5727     case 0x15b3:        // (blue)
5728       element = EL_CHAR_K;
5729       break;
5730
5731     case 0x15b4:        // (blue)
5732       element = EL_CHAR_L;
5733       break;
5734
5735     case 0x15b5:        // (blue)
5736       element = EL_CHAR_M;
5737       break;
5738
5739     case 0x15b6:        // (blue)
5740       element = EL_CHAR_N;
5741       break;
5742
5743     case 0x15b7:        // (blue)
5744       element = EL_CHAR_O;
5745       break;
5746
5747     case 0x15b8:        // (blue)
5748       element = EL_CHAR_P;
5749       break;
5750
5751     case 0x15b9:        // (blue)
5752       element = EL_CHAR_Q;
5753       break;
5754
5755     case 0x15ba:        // (blue)
5756       element = EL_CHAR_R;
5757       break;
5758
5759     case 0x15bb:        // (blue)
5760       element = EL_CHAR_S;
5761       break;
5762
5763     case 0x15bc:        // (blue)
5764       element = EL_CHAR_T;
5765       break;
5766
5767     case 0x15bd:        // (blue)
5768       element = EL_CHAR_U;
5769       break;
5770
5771     case 0x15be:        // (blue)
5772       element = EL_CHAR_V;
5773       break;
5774
5775     case 0x15bf:        // (blue)
5776       element = EL_CHAR_W;
5777       break;
5778
5779     case 0x15c0:        // (blue)
5780       element = EL_CHAR_X;
5781       break;
5782
5783     case 0x15c1:        // (blue)
5784       element = EL_CHAR_Y;
5785       break;
5786
5787     case 0x15c2:        // (blue)
5788       element = EL_CHAR_Z;
5789       break;
5790
5791     case 0x15c3:        // (blue)
5792       element = EL_CHAR_AUMLAUT;
5793       break;
5794
5795     case 0x15c4:        // (blue)
5796       element = EL_CHAR_OUMLAUT;
5797       break;
5798
5799     case 0x15c5:        // (blue)
5800       element = EL_CHAR_UUMLAUT;
5801       break;
5802
5803     case 0x15c6:        // (blue)
5804       element = EL_CHAR_0;
5805       break;
5806
5807     case 0x15c7:        // (blue)
5808       element = EL_CHAR_1;
5809       break;
5810
5811     case 0x15c8:        // (blue)
5812       element = EL_CHAR_2;
5813       break;
5814
5815     case 0x15c9:        // (blue)
5816       element = EL_CHAR_3;
5817       break;
5818
5819     case 0x15ca:        // (blue)
5820       element = EL_CHAR_4;
5821       break;
5822
5823     case 0x15cb:        // (blue)
5824       element = EL_CHAR_5;
5825       break;
5826
5827     case 0x15cc:        // (blue)
5828       element = EL_CHAR_6;
5829       break;
5830
5831     case 0x15cd:        // (blue)
5832       element = EL_CHAR_7;
5833       break;
5834
5835     case 0x15ce:        // (blue)
5836       element = EL_CHAR_8;
5837       break;
5838
5839     case 0x15cf:        // (blue)
5840       element = EL_CHAR_9;
5841       break;
5842
5843     case 0x15d0:        // (blue)
5844       element = EL_CHAR_PERIOD;
5845       break;
5846
5847     case 0x15d1:        // (blue)
5848       element = EL_CHAR_EXCLAM;
5849       break;
5850
5851     case 0x15d2:        // (blue)
5852       element = EL_CHAR_COLON;
5853       break;
5854
5855     case 0x15d3:        // (blue)
5856       element = EL_CHAR_LESS;
5857       break;
5858
5859     case 0x15d4:        // (blue)
5860       element = EL_CHAR_GREATER;
5861       break;
5862
5863     case 0x15d5:        // (blue)
5864       element = EL_CHAR_QUESTION;
5865       break;
5866
5867     case 0x15d6:        // (blue)
5868       element = EL_CHAR_COPYRIGHT;
5869       break;
5870
5871     case 0x15d7:        // (blue)
5872       element = EL_CHAR_UP;
5873       break;
5874
5875     case 0x15d8:        // (blue)
5876       element = EL_CHAR_DOWN;
5877       break;
5878
5879     case 0x15d9:        // (blue)
5880       element = EL_CHAR_BUTTON;
5881       break;
5882
5883     case 0x15da:        // (blue)
5884       element = EL_CHAR_PLUS;
5885       break;
5886
5887     case 0x15db:        // (blue)
5888       element = EL_CHAR_MINUS;
5889       break;
5890
5891     case 0x15dc:        // (blue)
5892       element = EL_CHAR_APOSTROPHE;
5893       break;
5894
5895     case 0x15dd:        // (blue)
5896       element = EL_CHAR_PARENLEFT;
5897       break;
5898
5899     case 0x15de:        // (blue)
5900       element = EL_CHAR_PARENRIGHT;
5901       break;
5902
5903     case 0x15df:        // (green)
5904       element = EL_CHAR_A;
5905       break;
5906
5907     case 0x15e0:        // (green)
5908       element = EL_CHAR_B;
5909       break;
5910
5911     case 0x15e1:        // (green)
5912       element = EL_CHAR_C;
5913       break;
5914
5915     case 0x15e2:        // (green)
5916       element = EL_CHAR_D;
5917       break;
5918
5919     case 0x15e3:        // (green)
5920       element = EL_CHAR_E;
5921       break;
5922
5923     case 0x15e4:        // (green)
5924       element = EL_CHAR_F;
5925       break;
5926
5927     case 0x15e5:        // (green)
5928       element = EL_CHAR_G;
5929       break;
5930
5931     case 0x15e6:        // (green)
5932       element = EL_CHAR_H;
5933       break;
5934
5935     case 0x15e7:        // (green)
5936       element = EL_CHAR_I;
5937       break;
5938
5939     case 0x15e8:        // (green)
5940       element = EL_CHAR_J;
5941       break;
5942
5943     case 0x15e9:        // (green)
5944       element = EL_CHAR_K;
5945       break;
5946
5947     case 0x15ea:        // (green)
5948       element = EL_CHAR_L;
5949       break;
5950
5951     case 0x15eb:        // (green)
5952       element = EL_CHAR_M;
5953       break;
5954
5955     case 0x15ec:        // (green)
5956       element = EL_CHAR_N;
5957       break;
5958
5959     case 0x15ed:        // (green)
5960       element = EL_CHAR_O;
5961       break;
5962
5963     case 0x15ee:        // (green)
5964       element = EL_CHAR_P;
5965       break;
5966
5967     case 0x15ef:        // (green)
5968       element = EL_CHAR_Q;
5969       break;
5970
5971     case 0x15f0:        // (green)
5972       element = EL_CHAR_R;
5973       break;
5974
5975     case 0x15f1:        // (green)
5976       element = EL_CHAR_S;
5977       break;
5978
5979     case 0x15f2:        // (green)
5980       element = EL_CHAR_T;
5981       break;
5982
5983     case 0x15f3:        // (green)
5984       element = EL_CHAR_U;
5985       break;
5986
5987     case 0x15f4:        // (green)
5988       element = EL_CHAR_V;
5989       break;
5990
5991     case 0x15f5:        // (green)
5992       element = EL_CHAR_W;
5993       break;
5994
5995     case 0x15f6:        // (green)
5996       element = EL_CHAR_X;
5997       break;
5998
5999     case 0x15f7:        // (green)
6000       element = EL_CHAR_Y;
6001       break;
6002
6003     case 0x15f8:        // (green)
6004       element = EL_CHAR_Z;
6005       break;
6006
6007     case 0x15f9:        // (green)
6008       element = EL_CHAR_AUMLAUT;
6009       break;
6010
6011     case 0x15fa:        // (green)
6012       element = EL_CHAR_OUMLAUT;
6013       break;
6014
6015     case 0x15fb:        // (green)
6016       element = EL_CHAR_UUMLAUT;
6017       break;
6018
6019     case 0x15fc:        // (green)
6020       element = EL_CHAR_0;
6021       break;
6022
6023     case 0x15fd:        // (green)
6024       element = EL_CHAR_1;
6025       break;
6026
6027     case 0x15fe:        // (green)
6028       element = EL_CHAR_2;
6029       break;
6030
6031     case 0x15ff:        // (green)
6032       element = EL_CHAR_3;
6033       break;
6034
6035     case 0x1600:        // (green)
6036       element = EL_CHAR_4;
6037       break;
6038
6039     case 0x1601:        // (green)
6040       element = EL_CHAR_5;
6041       break;
6042
6043     case 0x1602:        // (green)
6044       element = EL_CHAR_6;
6045       break;
6046
6047     case 0x1603:        // (green)
6048       element = EL_CHAR_7;
6049       break;
6050
6051     case 0x1604:        // (green)
6052       element = EL_CHAR_8;
6053       break;
6054
6055     case 0x1605:        // (green)
6056       element = EL_CHAR_9;
6057       break;
6058
6059     case 0x1606:        // (green)
6060       element = EL_CHAR_PERIOD;
6061       break;
6062
6063     case 0x1607:        // (green)
6064       element = EL_CHAR_EXCLAM;
6065       break;
6066
6067     case 0x1608:        // (green)
6068       element = EL_CHAR_COLON;
6069       break;
6070
6071     case 0x1609:        // (green)
6072       element = EL_CHAR_LESS;
6073       break;
6074
6075     case 0x160a:        // (green)
6076       element = EL_CHAR_GREATER;
6077       break;
6078
6079     case 0x160b:        // (green)
6080       element = EL_CHAR_QUESTION;
6081       break;
6082
6083     case 0x160c:        // (green)
6084       element = EL_CHAR_COPYRIGHT;
6085       break;
6086
6087     case 0x160d:        // (green)
6088       element = EL_CHAR_UP;
6089       break;
6090
6091     case 0x160e:        // (green)
6092       element = EL_CHAR_DOWN;
6093       break;
6094
6095     case 0x160f:        // (green)
6096       element = EL_CHAR_BUTTON;
6097       break;
6098
6099     case 0x1610:        // (green)
6100       element = EL_CHAR_PLUS;
6101       break;
6102
6103     case 0x1611:        // (green)
6104       element = EL_CHAR_MINUS;
6105       break;
6106
6107     case 0x1612:        // (green)
6108       element = EL_CHAR_APOSTROPHE;
6109       break;
6110
6111     case 0x1613:        // (green)
6112       element = EL_CHAR_PARENLEFT;
6113       break;
6114
6115     case 0x1614:        // (green)
6116       element = EL_CHAR_PARENRIGHT;
6117       break;
6118
6119     case 0x1615:        // (blue steel)
6120       element = EL_STEEL_CHAR_A;
6121       break;
6122
6123     case 0x1616:        // (blue steel)
6124       element = EL_STEEL_CHAR_B;
6125       break;
6126
6127     case 0x1617:        // (blue steel)
6128       element = EL_STEEL_CHAR_C;
6129       break;
6130
6131     case 0x1618:        // (blue steel)
6132       element = EL_STEEL_CHAR_D;
6133       break;
6134
6135     case 0x1619:        // (blue steel)
6136       element = EL_STEEL_CHAR_E;
6137       break;
6138
6139     case 0x161a:        // (blue steel)
6140       element = EL_STEEL_CHAR_F;
6141       break;
6142
6143     case 0x161b:        // (blue steel)
6144       element = EL_STEEL_CHAR_G;
6145       break;
6146
6147     case 0x161c:        // (blue steel)
6148       element = EL_STEEL_CHAR_H;
6149       break;
6150
6151     case 0x161d:        // (blue steel)
6152       element = EL_STEEL_CHAR_I;
6153       break;
6154
6155     case 0x161e:        // (blue steel)
6156       element = EL_STEEL_CHAR_J;
6157       break;
6158
6159     case 0x161f:        // (blue steel)
6160       element = EL_STEEL_CHAR_K;
6161       break;
6162
6163     case 0x1620:        // (blue steel)
6164       element = EL_STEEL_CHAR_L;
6165       break;
6166
6167     case 0x1621:        // (blue steel)
6168       element = EL_STEEL_CHAR_M;
6169       break;
6170
6171     case 0x1622:        // (blue steel)
6172       element = EL_STEEL_CHAR_N;
6173       break;
6174
6175     case 0x1623:        // (blue steel)
6176       element = EL_STEEL_CHAR_O;
6177       break;
6178
6179     case 0x1624:        // (blue steel)
6180       element = EL_STEEL_CHAR_P;
6181       break;
6182
6183     case 0x1625:        // (blue steel)
6184       element = EL_STEEL_CHAR_Q;
6185       break;
6186
6187     case 0x1626:        // (blue steel)
6188       element = EL_STEEL_CHAR_R;
6189       break;
6190
6191     case 0x1627:        // (blue steel)
6192       element = EL_STEEL_CHAR_S;
6193       break;
6194
6195     case 0x1628:        // (blue steel)
6196       element = EL_STEEL_CHAR_T;
6197       break;
6198
6199     case 0x1629:        // (blue steel)
6200       element = EL_STEEL_CHAR_U;
6201       break;
6202
6203     case 0x162a:        // (blue steel)
6204       element = EL_STEEL_CHAR_V;
6205       break;
6206
6207     case 0x162b:        // (blue steel)
6208       element = EL_STEEL_CHAR_W;
6209       break;
6210
6211     case 0x162c:        // (blue steel)
6212       element = EL_STEEL_CHAR_X;
6213       break;
6214
6215     case 0x162d:        // (blue steel)
6216       element = EL_STEEL_CHAR_Y;
6217       break;
6218
6219     case 0x162e:        // (blue steel)
6220       element = EL_STEEL_CHAR_Z;
6221       break;
6222
6223     case 0x162f:        // (blue steel)
6224       element = EL_STEEL_CHAR_AUMLAUT;
6225       break;
6226
6227     case 0x1630:        // (blue steel)
6228       element = EL_STEEL_CHAR_OUMLAUT;
6229       break;
6230
6231     case 0x1631:        // (blue steel)
6232       element = EL_STEEL_CHAR_UUMLAUT;
6233       break;
6234
6235     case 0x1632:        // (blue steel)
6236       element = EL_STEEL_CHAR_0;
6237       break;
6238
6239     case 0x1633:        // (blue steel)
6240       element = EL_STEEL_CHAR_1;
6241       break;
6242
6243     case 0x1634:        // (blue steel)
6244       element = EL_STEEL_CHAR_2;
6245       break;
6246
6247     case 0x1635:        // (blue steel)
6248       element = EL_STEEL_CHAR_3;
6249       break;
6250
6251     case 0x1636:        // (blue steel)
6252       element = EL_STEEL_CHAR_4;
6253       break;
6254
6255     case 0x1637:        // (blue steel)
6256       element = EL_STEEL_CHAR_5;
6257       break;
6258
6259     case 0x1638:        // (blue steel)
6260       element = EL_STEEL_CHAR_6;
6261       break;
6262
6263     case 0x1639:        // (blue steel)
6264       element = EL_STEEL_CHAR_7;
6265       break;
6266
6267     case 0x163a:        // (blue steel)
6268       element = EL_STEEL_CHAR_8;
6269       break;
6270
6271     case 0x163b:        // (blue steel)
6272       element = EL_STEEL_CHAR_9;
6273       break;
6274
6275     case 0x163c:        // (blue steel)
6276       element = EL_STEEL_CHAR_PERIOD;
6277       break;
6278
6279     case 0x163d:        // (blue steel)
6280       element = EL_STEEL_CHAR_EXCLAM;
6281       break;
6282
6283     case 0x163e:        // (blue steel)
6284       element = EL_STEEL_CHAR_COLON;
6285       break;
6286
6287     case 0x163f:        // (blue steel)
6288       element = EL_STEEL_CHAR_LESS;
6289       break;
6290
6291     case 0x1640:        // (blue steel)
6292       element = EL_STEEL_CHAR_GREATER;
6293       break;
6294
6295     case 0x1641:        // (blue steel)
6296       element = EL_STEEL_CHAR_QUESTION;
6297       break;
6298
6299     case 0x1642:        // (blue steel)
6300       element = EL_STEEL_CHAR_COPYRIGHT;
6301       break;
6302
6303     case 0x1643:        // (blue steel)
6304       element = EL_STEEL_CHAR_UP;
6305       break;
6306
6307     case 0x1644:        // (blue steel)
6308       element = EL_STEEL_CHAR_DOWN;
6309       break;
6310
6311     case 0x1645:        // (blue steel)
6312       element = EL_STEEL_CHAR_BUTTON;
6313       break;
6314
6315     case 0x1646:        // (blue steel)
6316       element = EL_STEEL_CHAR_PLUS;
6317       break;
6318
6319     case 0x1647:        // (blue steel)
6320       element = EL_STEEL_CHAR_MINUS;
6321       break;
6322
6323     case 0x1648:        // (blue steel)
6324       element = EL_STEEL_CHAR_APOSTROPHE;
6325       break;
6326
6327     case 0x1649:        // (blue steel)
6328       element = EL_STEEL_CHAR_PARENLEFT;
6329       break;
6330
6331     case 0x164a:        // (blue steel)
6332       element = EL_STEEL_CHAR_PARENRIGHT;
6333       break;
6334
6335     case 0x164b:        // (green steel)
6336       element = EL_STEEL_CHAR_A;
6337       break;
6338
6339     case 0x164c:        // (green steel)
6340       element = EL_STEEL_CHAR_B;
6341       break;
6342
6343     case 0x164d:        // (green steel)
6344       element = EL_STEEL_CHAR_C;
6345       break;
6346
6347     case 0x164e:        // (green steel)
6348       element = EL_STEEL_CHAR_D;
6349       break;
6350
6351     case 0x164f:        // (green steel)
6352       element = EL_STEEL_CHAR_E;
6353       break;
6354
6355     case 0x1650:        // (green steel)
6356       element = EL_STEEL_CHAR_F;
6357       break;
6358
6359     case 0x1651:        // (green steel)
6360       element = EL_STEEL_CHAR_G;
6361       break;
6362
6363     case 0x1652:        // (green steel)
6364       element = EL_STEEL_CHAR_H;
6365       break;
6366
6367     case 0x1653:        // (green steel)
6368       element = EL_STEEL_CHAR_I;
6369       break;
6370
6371     case 0x1654:        // (green steel)
6372       element = EL_STEEL_CHAR_J;
6373       break;
6374
6375     case 0x1655:        // (green steel)
6376       element = EL_STEEL_CHAR_K;
6377       break;
6378
6379     case 0x1656:        // (green steel)
6380       element = EL_STEEL_CHAR_L;
6381       break;
6382
6383     case 0x1657:        // (green steel)
6384       element = EL_STEEL_CHAR_M;
6385       break;
6386
6387     case 0x1658:        // (green steel)
6388       element = EL_STEEL_CHAR_N;
6389       break;
6390
6391     case 0x1659:        // (green steel)
6392       element = EL_STEEL_CHAR_O;
6393       break;
6394
6395     case 0x165a:        // (green steel)
6396       element = EL_STEEL_CHAR_P;
6397       break;
6398
6399     case 0x165b:        // (green steel)
6400       element = EL_STEEL_CHAR_Q;
6401       break;
6402
6403     case 0x165c:        // (green steel)
6404       element = EL_STEEL_CHAR_R;
6405       break;
6406
6407     case 0x165d:        // (green steel)
6408       element = EL_STEEL_CHAR_S;
6409       break;
6410
6411     case 0x165e:        // (green steel)
6412       element = EL_STEEL_CHAR_T;
6413       break;
6414
6415     case 0x165f:        // (green steel)
6416       element = EL_STEEL_CHAR_U;
6417       break;
6418
6419     case 0x1660:        // (green steel)
6420       element = EL_STEEL_CHAR_V;
6421       break;
6422
6423     case 0x1661:        // (green steel)
6424       element = EL_STEEL_CHAR_W;
6425       break;
6426
6427     case 0x1662:        // (green steel)
6428       element = EL_STEEL_CHAR_X;
6429       break;
6430
6431     case 0x1663:        // (green steel)
6432       element = EL_STEEL_CHAR_Y;
6433       break;
6434
6435     case 0x1664:        // (green steel)
6436       element = EL_STEEL_CHAR_Z;
6437       break;
6438
6439     case 0x1665:        // (green steel)
6440       element = EL_STEEL_CHAR_AUMLAUT;
6441       break;
6442
6443     case 0x1666:        // (green steel)
6444       element = EL_STEEL_CHAR_OUMLAUT;
6445       break;
6446
6447     case 0x1667:        // (green steel)
6448       element = EL_STEEL_CHAR_UUMLAUT;
6449       break;
6450
6451     case 0x1668:        // (green steel)
6452       element = EL_STEEL_CHAR_0;
6453       break;
6454
6455     case 0x1669:        // (green steel)
6456       element = EL_STEEL_CHAR_1;
6457       break;
6458
6459     case 0x166a:        // (green steel)
6460       element = EL_STEEL_CHAR_2;
6461       break;
6462
6463     case 0x166b:        // (green steel)
6464       element = EL_STEEL_CHAR_3;
6465       break;
6466
6467     case 0x166c:        // (green steel)
6468       element = EL_STEEL_CHAR_4;
6469       break;
6470
6471     case 0x166d:        // (green steel)
6472       element = EL_STEEL_CHAR_5;
6473       break;
6474
6475     case 0x166e:        // (green steel)
6476       element = EL_STEEL_CHAR_6;
6477       break;
6478
6479     case 0x166f:        // (green steel)
6480       element = EL_STEEL_CHAR_7;
6481       break;
6482
6483     case 0x1670:        // (green steel)
6484       element = EL_STEEL_CHAR_8;
6485       break;
6486
6487     case 0x1671:        // (green steel)
6488       element = EL_STEEL_CHAR_9;
6489       break;
6490
6491     case 0x1672:        // (green steel)
6492       element = EL_STEEL_CHAR_PERIOD;
6493       break;
6494
6495     case 0x1673:        // (green steel)
6496       element = EL_STEEL_CHAR_EXCLAM;
6497       break;
6498
6499     case 0x1674:        // (green steel)
6500       element = EL_STEEL_CHAR_COLON;
6501       break;
6502
6503     case 0x1675:        // (green steel)
6504       element = EL_STEEL_CHAR_LESS;
6505       break;
6506
6507     case 0x1676:        // (green steel)
6508       element = EL_STEEL_CHAR_GREATER;
6509       break;
6510
6511     case 0x1677:        // (green steel)
6512       element = EL_STEEL_CHAR_QUESTION;
6513       break;
6514
6515     case 0x1678:        // (green steel)
6516       element = EL_STEEL_CHAR_COPYRIGHT;
6517       break;
6518
6519     case 0x1679:        // (green steel)
6520       element = EL_STEEL_CHAR_UP;
6521       break;
6522
6523     case 0x167a:        // (green steel)
6524       element = EL_STEEL_CHAR_DOWN;
6525       break;
6526
6527     case 0x167b:        // (green steel)
6528       element = EL_STEEL_CHAR_BUTTON;
6529       break;
6530
6531     case 0x167c:        // (green steel)
6532       element = EL_STEEL_CHAR_PLUS;
6533       break;
6534
6535     case 0x167d:        // (green steel)
6536       element = EL_STEEL_CHAR_MINUS;
6537       break;
6538
6539     case 0x167e:        // (green steel)
6540       element = EL_STEEL_CHAR_APOSTROPHE;
6541       break;
6542
6543     case 0x167f:        // (green steel)
6544       element = EL_STEEL_CHAR_PARENLEFT;
6545       break;
6546
6547     case 0x1680:        // (green steel)
6548       element = EL_STEEL_CHAR_PARENRIGHT;
6549       break;
6550
6551     case 0x1681:        // gate (red)
6552       element = EL_EM_GATE_1;
6553       break;
6554
6555     case 0x1682:        // secret gate (red)
6556       element = EL_EM_GATE_1_GRAY;
6557       break;
6558
6559     case 0x1683:        // gate (yellow)
6560       element = EL_EM_GATE_2;
6561       break;
6562
6563     case 0x1684:        // secret gate (yellow)
6564       element = EL_EM_GATE_2_GRAY;
6565       break;
6566
6567     case 0x1685:        // gate (blue)
6568       element = EL_EM_GATE_4;
6569       break;
6570
6571     case 0x1686:        // secret gate (blue)
6572       element = EL_EM_GATE_4_GRAY;
6573       break;
6574
6575     case 0x1687:        // gate (green)
6576       element = EL_EM_GATE_3;
6577       break;
6578
6579     case 0x1688:        // secret gate (green)
6580       element = EL_EM_GATE_3_GRAY;
6581       break;
6582
6583     case 0x1689:        // gate (white)
6584       element = EL_DC_GATE_WHITE;
6585       break;
6586
6587     case 0x168a:        // secret gate (white)
6588       element = EL_DC_GATE_WHITE_GRAY;
6589       break;
6590
6591     case 0x168b:        // secret gate (no key)
6592       element = EL_DC_GATE_FAKE_GRAY;
6593       break;
6594
6595     case 0x168c:
6596       element = EL_ROBOT_WHEEL;
6597       break;
6598
6599     case 0x168d:
6600       element = EL_DC_TIMEGATE_SWITCH;
6601       break;
6602
6603     case 0x168e:
6604       element = EL_ACID_POOL_BOTTOM;
6605       break;
6606
6607     case 0x168f:
6608       element = EL_ACID_POOL_TOPLEFT;
6609       break;
6610
6611     case 0x1690:
6612       element = EL_ACID_POOL_TOPRIGHT;
6613       break;
6614
6615     case 0x1691:
6616       element = EL_ACID_POOL_BOTTOMLEFT;
6617       break;
6618
6619     case 0x1692:
6620       element = EL_ACID_POOL_BOTTOMRIGHT;
6621       break;
6622
6623     case 0x1693:
6624       element = EL_STEELWALL;
6625       break;
6626
6627     case 0x1694:
6628       element = EL_STEELWALL_SLIPPERY;
6629       break;
6630
6631     case 0x1695:        // steel wall (not round)
6632       element = EL_STEELWALL;
6633       break;
6634
6635     case 0x1696:        // steel wall (left)
6636       element = EL_DC_STEELWALL_1_LEFT;
6637       break;
6638
6639     case 0x1697:        // steel wall (bottom)
6640       element = EL_DC_STEELWALL_1_BOTTOM;
6641       break;
6642
6643     case 0x1698:        // steel wall (right)
6644       element = EL_DC_STEELWALL_1_RIGHT;
6645       break;
6646
6647     case 0x1699:        // steel wall (top)
6648       element = EL_DC_STEELWALL_1_TOP;
6649       break;
6650
6651     case 0x169a:        // steel wall (left/bottom)
6652       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6653       break;
6654
6655     case 0x169b:        // steel wall (right/bottom)
6656       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6657       break;
6658
6659     case 0x169c:        // steel wall (right/top)
6660       element = EL_DC_STEELWALL_1_TOPRIGHT;
6661       break;
6662
6663     case 0x169d:        // steel wall (left/top)
6664       element = EL_DC_STEELWALL_1_TOPLEFT;
6665       break;
6666
6667     case 0x169e:        // steel wall (right/bottom small)
6668       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6669       break;
6670
6671     case 0x169f:        // steel wall (left/bottom small)
6672       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6673       break;
6674
6675     case 0x16a0:        // steel wall (right/top small)
6676       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6677       break;
6678
6679     case 0x16a1:        // steel wall (left/top small)
6680       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6681       break;
6682
6683     case 0x16a2:        // steel wall (left/right)
6684       element = EL_DC_STEELWALL_1_VERTICAL;
6685       break;
6686
6687     case 0x16a3:        // steel wall (top/bottom)
6688       element = EL_DC_STEELWALL_1_HORIZONTAL;
6689       break;
6690
6691     case 0x16a4:        // steel wall 2 (left end)
6692       element = EL_DC_STEELWALL_2_LEFT;
6693       break;
6694
6695     case 0x16a5:        // steel wall 2 (right end)
6696       element = EL_DC_STEELWALL_2_RIGHT;
6697       break;
6698
6699     case 0x16a6:        // steel wall 2 (top end)
6700       element = EL_DC_STEELWALL_2_TOP;
6701       break;
6702
6703     case 0x16a7:        // steel wall 2 (bottom end)
6704       element = EL_DC_STEELWALL_2_BOTTOM;
6705       break;
6706
6707     case 0x16a8:        // steel wall 2 (left/right)
6708       element = EL_DC_STEELWALL_2_HORIZONTAL;
6709       break;
6710
6711     case 0x16a9:        // steel wall 2 (up/down)
6712       element = EL_DC_STEELWALL_2_VERTICAL;
6713       break;
6714
6715     case 0x16aa:        // steel wall 2 (mid)
6716       element = EL_DC_STEELWALL_2_MIDDLE;
6717       break;
6718
6719     case 0x16ab:
6720       element = EL_SIGN_EXCLAMATION;
6721       break;
6722
6723     case 0x16ac:
6724       element = EL_SIGN_RADIOACTIVITY;
6725       break;
6726
6727     case 0x16ad:
6728       element = EL_SIGN_STOP;
6729       break;
6730
6731     case 0x16ae:
6732       element = EL_SIGN_WHEELCHAIR;
6733       break;
6734
6735     case 0x16af:
6736       element = EL_SIGN_PARKING;
6737       break;
6738
6739     case 0x16b0:
6740       element = EL_SIGN_NO_ENTRY;
6741       break;
6742
6743     case 0x16b1:
6744       element = EL_SIGN_HEART;
6745       break;
6746
6747     case 0x16b2:
6748       element = EL_SIGN_GIVE_WAY;
6749       break;
6750
6751     case 0x16b3:
6752       element = EL_SIGN_ENTRY_FORBIDDEN;
6753       break;
6754
6755     case 0x16b4:
6756       element = EL_SIGN_EMERGENCY_EXIT;
6757       break;
6758
6759     case 0x16b5:
6760       element = EL_SIGN_YIN_YANG;
6761       break;
6762
6763     case 0x16b6:
6764       element = EL_WALL_EMERALD;
6765       break;
6766
6767     case 0x16b7:
6768       element = EL_WALL_DIAMOND;
6769       break;
6770
6771     case 0x16b8:
6772       element = EL_WALL_PEARL;
6773       break;
6774
6775     case 0x16b9:
6776       element = EL_WALL_CRYSTAL;
6777       break;
6778
6779     case 0x16ba:
6780       element = EL_INVISIBLE_WALL;
6781       break;
6782
6783     case 0x16bb:
6784       element = EL_INVISIBLE_STEELWALL;
6785       break;
6786
6787       // 0x16bc - 0x16cb:
6788       // EL_INVISIBLE_SAND
6789
6790     case 0x16cc:
6791       element = EL_LIGHT_SWITCH;
6792       break;
6793
6794     case 0x16cd:
6795       element = EL_ENVELOPE_1;
6796       break;
6797
6798     default:
6799       if (element >= 0x0117 && element <= 0x036e)       // (?)
6800         element = EL_DIAMOND;
6801       else if (element >= 0x042d && element <= 0x0684)  // (?)
6802         element = EL_EMERALD;
6803       else if (element >= 0x157c && element <= 0x158b)
6804         element = EL_SAND;
6805       else if (element >= 0x1590 && element <= 0x159f)
6806         element = EL_DC_LANDMINE;
6807       else if (element >= 0x16bc && element <= 0x16cb)
6808         element = EL_INVISIBLE_SAND;
6809       else
6810       {
6811         Warn("unknown Diamond Caves element 0x%04x", element);
6812
6813         element = EL_UNKNOWN;
6814       }
6815       break;
6816   }
6817
6818   return getMappedElement(element);
6819 }
6820
6821 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6822 {
6823   byte header[DC_LEVEL_HEADER_SIZE];
6824   int envelope_size;
6825   int envelope_header_pos = 62;
6826   int envelope_content_pos = 94;
6827   int level_name_pos = 251;
6828   int level_author_pos = 292;
6829   int envelope_header_len;
6830   int envelope_content_len;
6831   int level_name_len;
6832   int level_author_len;
6833   int fieldx, fieldy;
6834   int num_yamyam_contents;
6835   int i, x, y;
6836
6837   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6838
6839   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6840   {
6841     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6842
6843     header[i * 2 + 0] = header_word >> 8;
6844     header[i * 2 + 1] = header_word & 0xff;
6845   }
6846
6847   // read some values from level header to check level decoding integrity
6848   fieldx = header[6] | (header[7] << 8);
6849   fieldy = header[8] | (header[9] << 8);
6850   num_yamyam_contents = header[60] | (header[61] << 8);
6851
6852   // do some simple sanity checks to ensure that level was correctly decoded
6853   if (fieldx < 1 || fieldx > 256 ||
6854       fieldy < 1 || fieldy > 256 ||
6855       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6856   {
6857     level->no_valid_file = TRUE;
6858
6859     Warn("cannot decode level from stream -- using empty level");
6860
6861     return;
6862   }
6863
6864   // maximum envelope header size is 31 bytes
6865   envelope_header_len   = header[envelope_header_pos];
6866   // maximum envelope content size is 110 (156?) bytes
6867   envelope_content_len  = header[envelope_content_pos];
6868
6869   // maximum level title size is 40 bytes
6870   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6871   // maximum level author size is 30 (51?) bytes
6872   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6873
6874   envelope_size = 0;
6875
6876   for (i = 0; i < envelope_header_len; i++)
6877     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878       level->envelope[0].text[envelope_size++] =
6879         header[envelope_header_pos + 1 + i];
6880
6881   if (envelope_header_len > 0 && envelope_content_len > 0)
6882   {
6883     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6884       level->envelope[0].text[envelope_size++] = '\n';
6885     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6886       level->envelope[0].text[envelope_size++] = '\n';
6887   }
6888
6889   for (i = 0; i < envelope_content_len; i++)
6890     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6891       level->envelope[0].text[envelope_size++] =
6892         header[envelope_content_pos + 1 + i];
6893
6894   level->envelope[0].text[envelope_size] = '\0';
6895
6896   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6897   level->envelope[0].ysize = 10;
6898   level->envelope[0].autowrap = TRUE;
6899   level->envelope[0].centered = TRUE;
6900
6901   for (i = 0; i < level_name_len; i++)
6902     level->name[i] = header[level_name_pos + 1 + i];
6903   level->name[level_name_len] = '\0';
6904
6905   for (i = 0; i < level_author_len; i++)
6906     level->author[i] = header[level_author_pos + 1 + i];
6907   level->author[level_author_len] = '\0';
6908
6909   num_yamyam_contents = header[60] | (header[61] << 8);
6910   level->num_yamyam_contents =
6911     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6912
6913   for (i = 0; i < num_yamyam_contents; i++)
6914   {
6915     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6916     {
6917       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6918       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6919
6920       if (i < MAX_ELEMENT_CONTENTS)
6921         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6922     }
6923   }
6924
6925   fieldx = header[6] | (header[7] << 8);
6926   fieldy = header[8] | (header[9] << 8);
6927   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6928   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6929
6930   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6931   {
6932     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6933     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6934
6935     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6936       level->field[x][y] = getMappedElement_DC(element_dc);
6937   }
6938
6939   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941   level->field[x][y] = EL_PLAYER_1;
6942
6943   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6944   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6945   level->field[x][y] = EL_PLAYER_2;
6946
6947   level->gems_needed            = header[18] | (header[19] << 8);
6948
6949   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6950   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6951   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6952   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6953   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6954   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6955   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6956   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6957   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6958   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6959   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6960   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6961
6962   level->time                   = header[44] | (header[45] << 8);
6963
6964   level->amoeba_speed           = header[46] | (header[47] << 8);
6965   level->time_light             = header[48] | (header[49] << 8);
6966   level->time_timegate          = header[50] | (header[51] << 8);
6967   level->time_wheel             = header[52] | (header[53] << 8);
6968   level->time_magic_wall        = header[54] | (header[55] << 8);
6969   level->extra_time             = header[56] | (header[57] << 8);
6970   level->shield_normal_time     = header[58] | (header[59] << 8);
6971
6972   // shield and extra time elements do not have a score
6973   level->score[SC_SHIELD]       = 0;
6974   level->extra_time_score       = 0;
6975
6976   // set time for normal and deadly shields to the same value
6977   level->shield_deadly_time     = level->shield_normal_time;
6978
6979   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6980   // can slip down from flat walls, like normal walls and steel walls
6981   level->em_slippery_gems = TRUE;
6982
6983   // time score is counted for each 10 seconds left in Diamond Caves levels
6984   level->time_score_base = 10;
6985 }
6986
6987 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6988                                      struct LevelFileInfo *level_file_info,
6989                                      boolean level_info_only)
6990 {
6991   char *filename = level_file_info->filename;
6992   File *file;
6993   int num_magic_bytes = 8;
6994   char magic_bytes[num_magic_bytes + 1];
6995   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6996
6997   if (!(file = openFile(filename, MODE_READ)))
6998   {
6999     level->no_valid_file = TRUE;
7000
7001     if (!level_info_only)
7002       Warn("cannot read level '%s' -- using empty level", filename);
7003
7004     return;
7005   }
7006
7007   // fseek(file, 0x0000, SEEK_SET);
7008
7009   if (level_file_info->packed)
7010   {
7011     // read "magic bytes" from start of file
7012     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7013       magic_bytes[0] = '\0';
7014
7015     // check "magic bytes" for correct file format
7016     if (!strPrefix(magic_bytes, "DC2"))
7017     {
7018       level->no_valid_file = TRUE;
7019
7020       Warn("unknown DC level file '%s' -- using empty level", filename);
7021
7022       return;
7023     }
7024
7025     if (strPrefix(magic_bytes, "DC2Win95") ||
7026         strPrefix(magic_bytes, "DC2Win98"))
7027     {
7028       int position_first_level = 0x00fa;
7029       int extra_bytes = 4;
7030       int skip_bytes;
7031
7032       // advance file stream to first level inside the level package
7033       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7034
7035       // each block of level data is followed by block of non-level data
7036       num_levels_to_skip *= 2;
7037
7038       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7039       while (num_levels_to_skip >= 0)
7040       {
7041         // advance file stream to next level inside the level package
7042         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7043         {
7044           level->no_valid_file = TRUE;
7045
7046           Warn("cannot fseek in file '%s' -- using empty level", filename);
7047
7048           return;
7049         }
7050
7051         // skip apparently unused extra bytes following each level
7052         ReadUnusedBytesFromFile(file, extra_bytes);
7053
7054         // read size of next level in level package
7055         skip_bytes = getFile32BitLE(file);
7056
7057         num_levels_to_skip--;
7058       }
7059     }
7060     else
7061     {
7062       level->no_valid_file = TRUE;
7063
7064       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7065
7066       return;
7067     }
7068   }
7069
7070   LoadLevelFromFileStream_DC(file, level);
7071
7072   closeFile(file);
7073 }
7074
7075
7076 // ----------------------------------------------------------------------------
7077 // functions for loading SB level
7078 // ----------------------------------------------------------------------------
7079
7080 int getMappedElement_SB(int element_ascii, boolean use_ces)
7081 {
7082   static struct
7083   {
7084     int ascii;
7085     int sb;
7086     int ce;
7087   }
7088   sb_element_mapping[] =
7089   {
7090     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7091     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7092     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7093     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7094     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7095     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7096     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7097     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7098
7099     { 0,   -1,                      -1          },
7100   };
7101
7102   int i;
7103
7104   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7105     if (element_ascii == sb_element_mapping[i].ascii)
7106       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7107
7108   return EL_UNDEFINED;
7109 }
7110
7111 static void SetLevelSettings_SB(struct LevelInfo *level)
7112 {
7113   // time settings
7114   level->time = 0;
7115   level->use_step_counter = TRUE;
7116
7117   // score settings
7118   level->score[SC_TIME_BONUS] = 0;
7119   level->time_score_base = 1;
7120   level->rate_time_over_score = TRUE;
7121
7122   // game settings
7123   level->auto_exit_sokoban = TRUE;
7124 }
7125
7126 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7127                                      struct LevelFileInfo *level_file_info,
7128                                      boolean level_info_only)
7129 {
7130   char *filename = level_file_info->filename;
7131   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7132   char last_comment[MAX_LINE_LEN];
7133   char level_name[MAX_LINE_LEN];
7134   char *line_ptr;
7135   File *file;
7136   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7137   boolean read_continued_line = FALSE;
7138   boolean reading_playfield = FALSE;
7139   boolean got_valid_playfield_line = FALSE;
7140   boolean invalid_playfield_char = FALSE;
7141   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7142   int file_level_nr = 0;
7143   int x = 0, y = 0;             // initialized to make compilers happy
7144
7145   last_comment[0] = '\0';
7146   level_name[0] = '\0';
7147
7148   if (!(file = openFile(filename, MODE_READ)))
7149   {
7150     level->no_valid_file = TRUE;
7151
7152     if (!level_info_only)
7153       Warn("cannot read level '%s' -- using empty level", filename);
7154
7155     return;
7156   }
7157
7158   while (!checkEndOfFile(file))
7159   {
7160     // level successfully read, but next level may follow here
7161     if (!got_valid_playfield_line && reading_playfield)
7162     {
7163       // read playfield from single level file -- skip remaining file
7164       if (!level_file_info->packed)
7165         break;
7166
7167       if (file_level_nr >= num_levels_to_skip)
7168         break;
7169
7170       file_level_nr++;
7171
7172       last_comment[0] = '\0';
7173       level_name[0] = '\0';
7174
7175       reading_playfield = FALSE;
7176     }
7177
7178     got_valid_playfield_line = FALSE;
7179
7180     // read next line of input file
7181     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7182       break;
7183
7184     // cut trailing line break (this can be newline and/or carriage return)
7185     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7186       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7187         *line_ptr = '\0';
7188
7189     // copy raw input line for later use (mainly debugging output)
7190     strcpy(line_raw, line);
7191
7192     if (read_continued_line)
7193     {
7194       // append new line to existing line, if there is enough space
7195       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7196         strcat(previous_line, line_ptr);
7197
7198       strcpy(line, previous_line);      // copy storage buffer to line
7199
7200       read_continued_line = FALSE;
7201     }
7202
7203     // if the last character is '\', continue at next line
7204     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7205     {
7206       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7207       strcpy(previous_line, line);      // copy line to storage buffer
7208
7209       read_continued_line = TRUE;
7210
7211       continue;
7212     }
7213
7214     // skip empty lines
7215     if (line[0] == '\0')
7216       continue;
7217
7218     // extract comment text from comment line
7219     if (line[0] == ';')
7220     {
7221       for (line_ptr = line; *line_ptr; line_ptr++)
7222         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7223           break;
7224
7225       strcpy(last_comment, line_ptr);
7226
7227       continue;
7228     }
7229
7230     // extract level title text from line containing level title
7231     if (line[0] == '\'')
7232     {
7233       strcpy(level_name, &line[1]);
7234
7235       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7236         level_name[strlen(level_name) - 1] = '\0';
7237
7238       continue;
7239     }
7240
7241     // skip lines containing only spaces (or empty lines)
7242     for (line_ptr = line; *line_ptr; line_ptr++)
7243       if (*line_ptr != ' ')
7244         break;
7245     if (*line_ptr == '\0')
7246       continue;
7247
7248     // at this point, we have found a line containing part of a playfield
7249
7250     got_valid_playfield_line = TRUE;
7251
7252     if (!reading_playfield)
7253     {
7254       reading_playfield = TRUE;
7255       invalid_playfield_char = FALSE;
7256
7257       for (x = 0; x < MAX_LEV_FIELDX; x++)
7258         for (y = 0; y < MAX_LEV_FIELDY; y++)
7259           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7260
7261       level->fieldx = 0;
7262       level->fieldy = 0;
7263
7264       // start with topmost tile row
7265       y = 0;
7266     }
7267
7268     // skip playfield line if larger row than allowed
7269     if (y >= MAX_LEV_FIELDY)
7270       continue;
7271
7272     // start with leftmost tile column
7273     x = 0;
7274
7275     // read playfield elements from line
7276     for (line_ptr = line; *line_ptr; line_ptr++)
7277     {
7278       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7279
7280       // stop parsing playfield line if larger column than allowed
7281       if (x >= MAX_LEV_FIELDX)
7282         break;
7283
7284       if (mapped_sb_element == EL_UNDEFINED)
7285       {
7286         invalid_playfield_char = TRUE;
7287
7288         break;
7289       }
7290
7291       level->field[x][y] = mapped_sb_element;
7292
7293       // continue with next tile column
7294       x++;
7295
7296       level->fieldx = MAX(x, level->fieldx);
7297     }
7298
7299     if (invalid_playfield_char)
7300     {
7301       // if first playfield line, treat invalid lines as comment lines
7302       if (y == 0)
7303         reading_playfield = FALSE;
7304
7305       continue;
7306     }
7307
7308     // continue with next tile row
7309     y++;
7310   }
7311
7312   closeFile(file);
7313
7314   level->fieldy = y;
7315
7316   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7317   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7318
7319   if (!reading_playfield)
7320   {
7321     level->no_valid_file = TRUE;
7322
7323     Warn("cannot read level '%s' -- using empty level", filename);
7324
7325     return;
7326   }
7327
7328   if (*level_name != '\0')
7329   {
7330     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7331     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7332   }
7333   else if (*last_comment != '\0')
7334   {
7335     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7336     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7337   }
7338   else
7339   {
7340     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7341   }
7342
7343   // set all empty fields beyond the border walls to invisible steel wall
7344   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7345   {
7346     if ((x == 0 || x == level->fieldx - 1 ||
7347          y == 0 || y == level->fieldy - 1) &&
7348         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7349       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7350                      level->field, level->fieldx, level->fieldy);
7351   }
7352
7353   // set special level settings for Sokoban levels
7354   SetLevelSettings_SB(level);
7355
7356   if (load_xsb_to_ces)
7357   {
7358     // special global settings can now be set in level template
7359     level->use_custom_template = TRUE;
7360   }
7361 }
7362
7363
7364 // -------------------------------------------------------------------------
7365 // functions for handling native levels
7366 // -------------------------------------------------------------------------
7367
7368 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7369                                      struct LevelFileInfo *level_file_info,
7370                                      boolean level_info_only)
7371 {
7372   int pos = 0;
7373
7374   // determine position of requested level inside level package
7375   if (level_file_info->packed)
7376     pos = level_file_info->nr - leveldir_current->first_level;
7377
7378   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7379     level->no_valid_file = TRUE;
7380 }
7381
7382 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7383                                      struct LevelFileInfo *level_file_info,
7384                                      boolean level_info_only)
7385 {
7386   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7387     level->no_valid_file = TRUE;
7388 }
7389
7390 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7391                                      struct LevelFileInfo *level_file_info,
7392                                      boolean level_info_only)
7393 {
7394   int pos = 0;
7395
7396   // determine position of requested level inside level package
7397   if (level_file_info->packed)
7398     pos = level_file_info->nr - leveldir_current->first_level;
7399
7400   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7401     level->no_valid_file = TRUE;
7402 }
7403
7404 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7405                                      struct LevelFileInfo *level_file_info,
7406                                      boolean level_info_only)
7407 {
7408   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7409     level->no_valid_file = TRUE;
7410 }
7411
7412 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7413 {
7414   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7415     CopyNativeLevel_RND_to_BD(level);
7416   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7417     CopyNativeLevel_RND_to_EM(level);
7418   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7419     CopyNativeLevel_RND_to_SP(level);
7420   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7421     CopyNativeLevel_RND_to_MM(level);
7422 }
7423
7424 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7425 {
7426   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7427     CopyNativeLevel_BD_to_RND(level);
7428   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7429     CopyNativeLevel_EM_to_RND(level);
7430   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7431     CopyNativeLevel_SP_to_RND(level);
7432   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7433     CopyNativeLevel_MM_to_RND(level);
7434 }
7435
7436 void SaveNativeLevel(struct LevelInfo *level)
7437 {
7438   // saving native level files only supported for some game engines
7439   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7440       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7441     return;
7442
7443   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7444                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7445   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7446   char *filename = getLevelFilenameFromBasename(basename);
7447
7448   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7449     return;
7450
7451   boolean success = FALSE;
7452
7453   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7454   {
7455     CopyNativeLevel_RND_to_BD(level);
7456     // CopyNativeTape_RND_to_BD(level);
7457
7458     success = SaveNativeLevel_BD(filename);
7459   }
7460   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7461   {
7462     CopyNativeLevel_RND_to_SP(level);
7463     CopyNativeTape_RND_to_SP(level);
7464
7465     success = SaveNativeLevel_SP(filename);
7466   }
7467
7468   if (success)
7469     Request("Native level file saved!", REQ_CONFIRM);
7470   else
7471     Request("Failed to save native level file!", REQ_CONFIRM);
7472 }
7473
7474
7475 // ----------------------------------------------------------------------------
7476 // functions for loading generic level
7477 // ----------------------------------------------------------------------------
7478
7479 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7480                                   struct LevelFileInfo *level_file_info,
7481                                   boolean level_info_only)
7482 {
7483   // always start with reliable default values
7484   setLevelInfoToDefaults(level, level_info_only, TRUE);
7485
7486   switch (level_file_info->type)
7487   {
7488     case LEVEL_FILE_TYPE_RND:
7489       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7490       break;
7491
7492     case LEVEL_FILE_TYPE_BD:
7493       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7494       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7495       break;
7496
7497     case LEVEL_FILE_TYPE_EM:
7498       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7499       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7500       break;
7501
7502     case LEVEL_FILE_TYPE_SP:
7503       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7504       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7505       break;
7506
7507     case LEVEL_FILE_TYPE_MM:
7508       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7509       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7510       break;
7511
7512     case LEVEL_FILE_TYPE_DC:
7513       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7514       break;
7515
7516     case LEVEL_FILE_TYPE_SB:
7517       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7518       break;
7519
7520     default:
7521       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7522       break;
7523   }
7524
7525   // if level file is invalid, restore level structure to default values
7526   if (level->no_valid_file)
7527     setLevelInfoToDefaults(level, level_info_only, FALSE);
7528
7529   if (check_special_flags("use_native_bd_game_engine"))
7530     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7531
7532   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7533     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7534
7535   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7536     CopyNativeLevel_Native_to_RND(level);
7537 }
7538
7539 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7540 {
7541   static struct LevelFileInfo level_file_info;
7542
7543   // always start with reliable default values
7544   setFileInfoToDefaults(&level_file_info);
7545
7546   level_file_info.nr = 0;                       // unknown level number
7547   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7548
7549   setString(&level_file_info.filename, filename);
7550
7551   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7552 }
7553
7554 static void LoadLevel_InitVersion(struct LevelInfo *level)
7555 {
7556   int i, j;
7557
7558   if (leveldir_current == NULL)         // only when dumping level
7559     return;
7560
7561   // all engine modifications also valid for levels which use latest engine
7562   if (level->game_version < VERSION_IDENT(3,2,0,5))
7563   {
7564     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7565     level->time_score_base = 10;
7566   }
7567
7568   if (leveldir_current->latest_engine)
7569   {
7570     // ---------- use latest game engine --------------------------------------
7571
7572     /* For all levels which are forced to use the latest game engine version
7573        (normally all but user contributed, private and undefined levels), set
7574        the game engine version to the actual version; this allows for actual
7575        corrections in the game engine to take effect for existing, converted
7576        levels (from "classic" or other existing games) to make the emulation
7577        of the corresponding game more accurate, while (hopefully) not breaking
7578        existing levels created from other players. */
7579
7580     level->game_version = GAME_VERSION_ACTUAL;
7581
7582     /* Set special EM style gems behaviour: EM style gems slip down from
7583        normal, steel and growing wall. As this is a more fundamental change,
7584        it seems better to set the default behaviour to "off" (as it is more
7585        natural) and make it configurable in the level editor (as a property
7586        of gem style elements). Already existing converted levels (neither
7587        private nor contributed levels) are changed to the new behaviour. */
7588
7589     if (level->file_version < FILE_VERSION_2_0)
7590       level->em_slippery_gems = TRUE;
7591
7592     return;
7593   }
7594
7595   // ---------- use game engine the level was created with --------------------
7596
7597   /* For all levels which are not forced to use the latest game engine
7598      version (normally user contributed, private and undefined levels),
7599      use the version of the game engine the levels were created for.
7600
7601      Since 2.0.1, the game engine version is now directly stored
7602      in the level file (chunk "VERS"), so there is no need anymore
7603      to set the game version from the file version (except for old,
7604      pre-2.0 levels, where the game version is still taken from the
7605      file format version used to store the level -- see above). */
7606
7607   // player was faster than enemies in 1.0.0 and before
7608   if (level->file_version == FILE_VERSION_1_0)
7609     for (i = 0; i < MAX_PLAYERS; i++)
7610       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7611
7612   // default behaviour for EM style gems was "slippery" only in 2.0.1
7613   if (level->game_version == VERSION_IDENT(2,0,1,0))
7614     level->em_slippery_gems = TRUE;
7615
7616   // springs could be pushed over pits before (pre-release version) 2.2.0
7617   if (level->game_version < VERSION_IDENT(2,2,0,0))
7618     level->use_spring_bug = TRUE;
7619
7620   if (level->game_version < VERSION_IDENT(3,2,0,5))
7621   {
7622     // time orb caused limited time in endless time levels before 3.2.0-5
7623     level->use_time_orb_bug = TRUE;
7624
7625     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7626     level->block_snap_field = FALSE;
7627
7628     // extra time score was same value as time left score before 3.2.0-5
7629     level->extra_time_score = level->score[SC_TIME_BONUS];
7630   }
7631
7632   if (level->game_version < VERSION_IDENT(3,2,0,7))
7633   {
7634     // default behaviour for snapping was "not continuous" before 3.2.0-7
7635     level->continuous_snapping = FALSE;
7636   }
7637
7638   // only few elements were able to actively move into acid before 3.1.0
7639   // trigger settings did not exist before 3.1.0; set to default "any"
7640   if (level->game_version < VERSION_IDENT(3,1,0,0))
7641   {
7642     // correct "can move into acid" settings (all zero in old levels)
7643
7644     level->can_move_into_acid_bits = 0; // nothing can move into acid
7645     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7646
7647     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7648     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7649     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7650     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7651
7652     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7653       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7654
7655     // correct trigger settings (stored as zero == "none" in old levels)
7656
7657     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7658     {
7659       int element = EL_CUSTOM_START + i;
7660       struct ElementInfo *ei = &element_info[element];
7661
7662       for (j = 0; j < ei->num_change_pages; j++)
7663       {
7664         struct ElementChangeInfo *change = &ei->change_page[j];
7665
7666         change->trigger_player = CH_PLAYER_ANY;
7667         change->trigger_page = CH_PAGE_ANY;
7668       }
7669     }
7670   }
7671
7672   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7673   {
7674     int element = EL_CUSTOM_256;
7675     struct ElementInfo *ei = &element_info[element];
7676     struct ElementChangeInfo *change = &ei->change_page[0];
7677
7678     /* This is needed to fix a problem that was caused by a bugfix in function
7679        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7680        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7681        not replace walkable elements, but instead just placed the player on it,
7682        without placing the Sokoban field under the player). Unfortunately, this
7683        breaks "Snake Bite" style levels when the snake is halfway through a door
7684        that just closes (the snake head is still alive and can be moved in this
7685        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7686        player (without Sokoban element) which then gets killed as designed). */
7687
7688     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7689          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7690         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7691       change->target_element = EL_PLAYER_1;
7692   }
7693
7694   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7695   if (level->game_version < VERSION_IDENT(3,2,5,0))
7696   {
7697     /* This is needed to fix a problem that was caused by a bugfix in function
7698        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7699        corrects the behaviour when a custom element changes to another custom
7700        element with a higher element number that has change actions defined.
7701        Normally, only one change per frame is allowed for custom elements.
7702        Therefore, it is checked if a custom element already changed in the
7703        current frame; if it did, subsequent changes are suppressed.
7704        Unfortunately, this is only checked for element changes, but not for
7705        change actions, which are still executed. As the function above loops
7706        through all custom elements from lower to higher, an element change
7707        resulting in a lower CE number won't be checked again, while a target
7708        element with a higher number will also be checked, and potential change
7709        actions will get executed for this CE, too (which is wrong), while
7710        further changes are ignored (which is correct). As this bugfix breaks
7711        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7712        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7713        behaviour for existing levels and tapes that make use of this bug */
7714
7715     level->use_action_after_change_bug = TRUE;
7716   }
7717
7718   // not centering level after relocating player was default only in 3.2.3
7719   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7720     level->shifted_relocation = TRUE;
7721
7722   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7723   if (level->game_version < VERSION_IDENT(3,2,6,0))
7724     level->em_explodes_by_fire = TRUE;
7725
7726   // levels were solved by the first player entering an exit up to 4.1.0.0
7727   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7728     level->solved_by_one_player = TRUE;
7729
7730   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7731   if (level->game_version < VERSION_IDENT(4,1,1,1))
7732     level->use_life_bugs = TRUE;
7733
7734   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7735   if (level->game_version < VERSION_IDENT(4,1,1,1))
7736     level->sb_objects_needed = FALSE;
7737
7738   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7739   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7740     level->finish_dig_collect = FALSE;
7741
7742   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7743   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7744     level->keep_walkable_ce = TRUE;
7745 }
7746
7747 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7748 {
7749   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7750   int x, y;
7751
7752   // check if this level is (not) a Sokoban level
7753   for (y = 0; y < level->fieldy; y++)
7754     for (x = 0; x < level->fieldx; x++)
7755       if (!IS_SB_ELEMENT(Tile[x][y]))
7756         is_sokoban_level = FALSE;
7757
7758   if (is_sokoban_level)
7759   {
7760     // set special level settings for Sokoban levels
7761     SetLevelSettings_SB(level);
7762   }
7763 }
7764
7765 static void LoadLevel_InitSettings(struct LevelInfo *level)
7766 {
7767   // adjust level settings for (non-native) Sokoban-style levels
7768   LoadLevel_InitSettings_SB(level);
7769
7770   // rename levels with title "nameless level" or if renaming is forced
7771   if (leveldir_current->empty_level_name != NULL &&
7772       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7773        leveldir_current->force_level_name))
7774     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7775              leveldir_current->empty_level_name, level_nr);
7776 }
7777
7778 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7779 {
7780   int i, x, y;
7781
7782   // map elements that have changed in newer versions
7783   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7784                                                     level->game_version);
7785   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7786     for (x = 0; x < 3; x++)
7787       for (y = 0; y < 3; y++)
7788         level->yamyam_content[i].e[x][y] =
7789           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7790                                     level->game_version);
7791
7792 }
7793
7794 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7795 {
7796   int i, j;
7797
7798   // map custom element change events that have changed in newer versions
7799   // (these following values were accidentally changed in version 3.0.1)
7800   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7801   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7802   {
7803     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7804     {
7805       int element = EL_CUSTOM_START + i;
7806
7807       // order of checking and copying events to be mapped is important
7808       // (do not change the start and end value -- they are constant)
7809       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7810       {
7811         if (HAS_CHANGE_EVENT(element, j - 2))
7812         {
7813           SET_CHANGE_EVENT(element, j - 2, FALSE);
7814           SET_CHANGE_EVENT(element, j, TRUE);
7815         }
7816       }
7817
7818       // order of checking and copying events to be mapped is important
7819       // (do not change the start and end value -- they are constant)
7820       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7821       {
7822         if (HAS_CHANGE_EVENT(element, j - 1))
7823         {
7824           SET_CHANGE_EVENT(element, j - 1, FALSE);
7825           SET_CHANGE_EVENT(element, j, TRUE);
7826         }
7827       }
7828     }
7829   }
7830
7831   // initialize "can_change" field for old levels with only one change page
7832   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7833   {
7834     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7835     {
7836       int element = EL_CUSTOM_START + i;
7837
7838       if (CAN_CHANGE(element))
7839         element_info[element].change->can_change = TRUE;
7840     }
7841   }
7842
7843   // correct custom element values (for old levels without these options)
7844   if (level->game_version < VERSION_IDENT(3,1,1,0))
7845   {
7846     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7847     {
7848       int element = EL_CUSTOM_START + i;
7849       struct ElementInfo *ei = &element_info[element];
7850
7851       if (ei->access_direction == MV_NO_DIRECTION)
7852         ei->access_direction = MV_ALL_DIRECTIONS;
7853     }
7854   }
7855
7856   // correct custom element values (fix invalid values for all versions)
7857   if (1)
7858   {
7859     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7860     {
7861       int element = EL_CUSTOM_START + i;
7862       struct ElementInfo *ei = &element_info[element];
7863
7864       for (j = 0; j < ei->num_change_pages; j++)
7865       {
7866         struct ElementChangeInfo *change = &ei->change_page[j];
7867
7868         if (change->trigger_player == CH_PLAYER_NONE)
7869           change->trigger_player = CH_PLAYER_ANY;
7870
7871         if (change->trigger_side == CH_SIDE_NONE)
7872           change->trigger_side = CH_SIDE_ANY;
7873       }
7874     }
7875   }
7876
7877   // initialize "can_explode" field for old levels which did not store this
7878   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7879   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7880   {
7881     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7882     {
7883       int element = EL_CUSTOM_START + i;
7884
7885       if (EXPLODES_1X1_OLD(element))
7886         element_info[element].explosion_type = EXPLODES_1X1;
7887
7888       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7889                                              EXPLODES_SMASHED(element) ||
7890                                              EXPLODES_IMPACT(element)));
7891     }
7892   }
7893
7894   // correct previously hard-coded move delay values for maze runner style
7895   if (level->game_version < VERSION_IDENT(3,1,1,0))
7896   {
7897     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7898     {
7899       int element = EL_CUSTOM_START + i;
7900
7901       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7902       {
7903         // previously hard-coded and therefore ignored
7904         element_info[element].move_delay_fixed = 9;
7905         element_info[element].move_delay_random = 0;
7906       }
7907     }
7908   }
7909
7910   // set some other uninitialized values of custom elements in older levels
7911   if (level->game_version < VERSION_IDENT(3,1,0,0))
7912   {
7913     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7914     {
7915       int element = EL_CUSTOM_START + i;
7916
7917       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7918
7919       element_info[element].explosion_delay = 17;
7920       element_info[element].ignition_delay = 8;
7921     }
7922   }
7923
7924   // set mouse click change events to work for left/middle/right mouse button
7925   if (level->game_version < VERSION_IDENT(4,2,3,0))
7926   {
7927     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7928     {
7929       int element = EL_CUSTOM_START + i;
7930       struct ElementInfo *ei = &element_info[element];
7931
7932       for (j = 0; j < ei->num_change_pages; j++)
7933       {
7934         struct ElementChangeInfo *change = &ei->change_page[j];
7935
7936         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7937             change->has_event[CE_PRESSED_BY_MOUSE] ||
7938             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7939             change->has_event[CE_MOUSE_PRESSED_ON_X])
7940           change->trigger_side = CH_SIDE_ANY;
7941       }
7942     }
7943   }
7944 }
7945
7946 static void LoadLevel_InitElements(struct LevelInfo *level)
7947 {
7948   LoadLevel_InitStandardElements(level);
7949
7950   if (level->file_has_custom_elements)
7951     LoadLevel_InitCustomElements(level);
7952
7953   // initialize element properties for level editor etc.
7954   InitElementPropertiesEngine(level->game_version);
7955   InitElementPropertiesGfxElement();
7956 }
7957
7958 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7959 {
7960   int x, y;
7961
7962   // map elements that have changed in newer versions
7963   for (y = 0; y < level->fieldy; y++)
7964     for (x = 0; x < level->fieldx; x++)
7965       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7966                                                      level->game_version);
7967
7968   // clear unused playfield data (nicer if level gets resized in editor)
7969   for (x = 0; x < MAX_LEV_FIELDX; x++)
7970     for (y = 0; y < MAX_LEV_FIELDY; y++)
7971       if (x >= level->fieldx || y >= level->fieldy)
7972         level->field[x][y] = EL_EMPTY;
7973
7974   // copy elements to runtime playfield array
7975   for (x = 0; x < MAX_LEV_FIELDX; x++)
7976     for (y = 0; y < MAX_LEV_FIELDY; y++)
7977       Tile[x][y] = level->field[x][y];
7978
7979   // initialize level size variables for faster access
7980   lev_fieldx = level->fieldx;
7981   lev_fieldy = level->fieldy;
7982
7983   // determine border element for this level
7984   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7985     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7986   else
7987     SetBorderElement();
7988 }
7989
7990 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7991 {
7992   struct LevelFileInfo *level_file_info = &level->file_info;
7993
7994   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7995     CopyNativeLevel_RND_to_Native(level);
7996 }
7997
7998 static void LoadLevelTemplate_LoadAndInit(void)
7999 {
8000   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8001
8002   LoadLevel_InitVersion(&level_template);
8003   LoadLevel_InitElements(&level_template);
8004   LoadLevel_InitSettings(&level_template);
8005
8006   ActivateLevelTemplate();
8007 }
8008
8009 void LoadLevelTemplate(int nr)
8010 {
8011   if (!fileExists(getGlobalLevelTemplateFilename()))
8012   {
8013     Warn("no level template found for this level");
8014
8015     return;
8016   }
8017
8018   setLevelFileInfo(&level_template.file_info, nr);
8019
8020   LoadLevelTemplate_LoadAndInit();
8021 }
8022
8023 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8024 {
8025   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8026
8027   LoadLevelTemplate_LoadAndInit();
8028 }
8029
8030 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8031 {
8032   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8033
8034   if (level.use_custom_template)
8035   {
8036     if (network_level != NULL)
8037       LoadNetworkLevelTemplate(network_level);
8038     else
8039       LoadLevelTemplate(-1);
8040   }
8041
8042   LoadLevel_InitVersion(&level);
8043   LoadLevel_InitElements(&level);
8044   LoadLevel_InitPlayfield(&level);
8045   LoadLevel_InitSettings(&level);
8046
8047   LoadLevel_InitNativeEngines(&level);
8048 }
8049
8050 void LoadLevel(int nr)
8051 {
8052   SetLevelSetInfo(leveldir_current->identifier, nr);
8053
8054   setLevelFileInfo(&level.file_info, nr);
8055
8056   LoadLevel_LoadAndInit(NULL);
8057 }
8058
8059 void LoadLevelInfoOnly(int nr)
8060 {
8061   setLevelFileInfo(&level.file_info, nr);
8062
8063   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8064 }
8065
8066 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8067 {
8068   SetLevelSetInfo(network_level->leveldir_identifier,
8069                   network_level->file_info.nr);
8070
8071   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8072
8073   LoadLevel_LoadAndInit(network_level);
8074 }
8075
8076 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8077 {
8078   int chunk_size = 0;
8079
8080   chunk_size += putFileVersion(file, level->file_version);
8081   chunk_size += putFileVersion(file, level->game_version);
8082
8083   return chunk_size;
8084 }
8085
8086 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8087 {
8088   int chunk_size = 0;
8089
8090   chunk_size += putFile16BitBE(file, level->creation_date.year);
8091   chunk_size += putFile8Bit(file,    level->creation_date.month);
8092   chunk_size += putFile8Bit(file,    level->creation_date.day);
8093
8094   return chunk_size;
8095 }
8096
8097 #if ENABLE_HISTORIC_CHUNKS
8098 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8099 {
8100   int i, x, y;
8101
8102   putFile8Bit(file, level->fieldx);
8103   putFile8Bit(file, level->fieldy);
8104
8105   putFile16BitBE(file, level->time);
8106   putFile16BitBE(file, level->gems_needed);
8107
8108   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8109     putFile8Bit(file, level->name[i]);
8110
8111   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8112     putFile8Bit(file, level->score[i]);
8113
8114   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8115     for (y = 0; y < 3; y++)
8116       for (x = 0; x < 3; x++)
8117         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8118                            level->yamyam_content[i].e[x][y]));
8119   putFile8Bit(file, level->amoeba_speed);
8120   putFile8Bit(file, level->time_magic_wall);
8121   putFile8Bit(file, level->time_wheel);
8122   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8123                      level->amoeba_content));
8124   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8125   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8126   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8127   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8128
8129   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8130
8131   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8132   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8133   putFile32BitBE(file, level->can_move_into_acid_bits);
8134   putFile8Bit(file, level->dont_collide_with_bits);
8135
8136   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8137   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8138
8139   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8140   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8141   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8142
8143   putFile8Bit(file, level->game_engine_type);
8144
8145   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8146 }
8147 #endif
8148
8149 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8150 {
8151   int chunk_size = 0;
8152   int i;
8153
8154   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8155     chunk_size += putFile8Bit(file, level->name[i]);
8156
8157   return chunk_size;
8158 }
8159
8160 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8161 {
8162   int chunk_size = 0;
8163   int i;
8164
8165   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8166     chunk_size += putFile8Bit(file, level->author[i]);
8167
8168   return chunk_size;
8169 }
8170
8171 #if ENABLE_HISTORIC_CHUNKS
8172 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8173 {
8174   int chunk_size = 0;
8175   int x, y;
8176
8177   for (y = 0; y < level->fieldy; y++)
8178     for (x = 0; x < level->fieldx; x++)
8179       if (level->encoding_16bit_field)
8180         chunk_size += putFile16BitBE(file, level->field[x][y]);
8181       else
8182         chunk_size += putFile8Bit(file, level->field[x][y]);
8183
8184   return chunk_size;
8185 }
8186 #endif
8187
8188 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8189 {
8190   int chunk_size = 0;
8191   int x, y;
8192
8193   for (y = 0; y < level->fieldy; y++) 
8194     for (x = 0; x < level->fieldx; x++) 
8195       chunk_size += putFile16BitBE(file, level->field[x][y]);
8196
8197   return chunk_size;
8198 }
8199
8200 #if ENABLE_HISTORIC_CHUNKS
8201 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8202 {
8203   int i, x, y;
8204
8205   putFile8Bit(file, EL_YAMYAM);
8206   putFile8Bit(file, level->num_yamyam_contents);
8207   putFile8Bit(file, 0);
8208   putFile8Bit(file, 0);
8209
8210   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8211     for (y = 0; y < 3; y++)
8212       for (x = 0; x < 3; x++)
8213         if (level->encoding_16bit_field)
8214           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8215         else
8216           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8217 }
8218 #endif
8219
8220 #if ENABLE_HISTORIC_CHUNKS
8221 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8222 {
8223   int i, x, y;
8224   int num_contents, content_xsize, content_ysize;
8225   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8226
8227   if (element == EL_YAMYAM)
8228   {
8229     num_contents = level->num_yamyam_contents;
8230     content_xsize = 3;
8231     content_ysize = 3;
8232
8233     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8234       for (y = 0; y < 3; y++)
8235         for (x = 0; x < 3; x++)
8236           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8237   }
8238   else if (element == EL_BD_AMOEBA)
8239   {
8240     num_contents = 1;
8241     content_xsize = 1;
8242     content_ysize = 1;
8243
8244     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8245       for (y = 0; y < 3; y++)
8246         for (x = 0; x < 3; x++)
8247           content_array[i][x][y] = EL_EMPTY;
8248     content_array[0][0][0] = level->amoeba_content;
8249   }
8250   else
8251   {
8252     // chunk header already written -- write empty chunk data
8253     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8254
8255     Warn("cannot save content for element '%d'", element);
8256
8257     return;
8258   }
8259
8260   putFile16BitBE(file, element);
8261   putFile8Bit(file, num_contents);
8262   putFile8Bit(file, content_xsize);
8263   putFile8Bit(file, content_ysize);
8264
8265   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8266
8267   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8268     for (y = 0; y < 3; y++)
8269       for (x = 0; x < 3; x++)
8270         putFile16BitBE(file, content_array[i][x][y]);
8271 }
8272 #endif
8273
8274 #if ENABLE_HISTORIC_CHUNKS
8275 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8276 {
8277   int envelope_nr = element - EL_ENVELOPE_1;
8278   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8279   int chunk_size = 0;
8280   int i;
8281
8282   chunk_size += putFile16BitBE(file, element);
8283   chunk_size += putFile16BitBE(file, envelope_len);
8284   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8285   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8286
8287   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8288   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8289
8290   for (i = 0; i < envelope_len; i++)
8291     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8292
8293   return chunk_size;
8294 }
8295 #endif
8296
8297 #if ENABLE_HISTORIC_CHUNKS
8298 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8299                            int num_changed_custom_elements)
8300 {
8301   int i, check = 0;
8302
8303   putFile16BitBE(file, num_changed_custom_elements);
8304
8305   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8306   {
8307     int element = EL_CUSTOM_START + i;
8308
8309     struct ElementInfo *ei = &element_info[element];
8310
8311     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8312     {
8313       if (check < num_changed_custom_elements)
8314       {
8315         putFile16BitBE(file, element);
8316         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8317       }
8318
8319       check++;
8320     }
8321   }
8322
8323   if (check != num_changed_custom_elements)     // should not happen
8324     Warn("inconsistent number of custom element properties");
8325 }
8326 #endif
8327
8328 #if ENABLE_HISTORIC_CHUNKS
8329 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8330                            int num_changed_custom_elements)
8331 {
8332   int i, check = 0;
8333
8334   putFile16BitBE(file, num_changed_custom_elements);
8335
8336   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8337   {
8338     int element = EL_CUSTOM_START + i;
8339
8340     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8341     {
8342       if (check < num_changed_custom_elements)
8343       {
8344         putFile16BitBE(file, element);
8345         putFile16BitBE(file, element_info[element].change->target_element);
8346       }
8347
8348       check++;
8349     }
8350   }
8351
8352   if (check != num_changed_custom_elements)     // should not happen
8353     Warn("inconsistent number of custom target elements");
8354 }
8355 #endif
8356
8357 #if ENABLE_HISTORIC_CHUNKS
8358 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8359                            int num_changed_custom_elements)
8360 {
8361   int i, j, x, y, check = 0;
8362
8363   putFile16BitBE(file, num_changed_custom_elements);
8364
8365   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8366   {
8367     int element = EL_CUSTOM_START + i;
8368     struct ElementInfo *ei = &element_info[element];
8369
8370     if (ei->modified_settings)
8371     {
8372       if (check < num_changed_custom_elements)
8373       {
8374         putFile16BitBE(file, element);
8375
8376         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8377           putFile8Bit(file, ei->description[j]);
8378
8379         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8380
8381         // some free bytes for future properties and padding
8382         WriteUnusedBytesToFile(file, 7);
8383
8384         putFile8Bit(file, ei->use_gfx_element);
8385         putFile16BitBE(file, ei->gfx_element_initial);
8386
8387         putFile8Bit(file, ei->collect_score_initial);
8388         putFile8Bit(file, ei->collect_count_initial);
8389
8390         putFile16BitBE(file, ei->push_delay_fixed);
8391         putFile16BitBE(file, ei->push_delay_random);
8392         putFile16BitBE(file, ei->move_delay_fixed);
8393         putFile16BitBE(file, ei->move_delay_random);
8394
8395         putFile16BitBE(file, ei->move_pattern);
8396         putFile8Bit(file, ei->move_direction_initial);
8397         putFile8Bit(file, ei->move_stepsize);
8398
8399         for (y = 0; y < 3; y++)
8400           for (x = 0; x < 3; x++)
8401             putFile16BitBE(file, ei->content.e[x][y]);
8402
8403         putFile32BitBE(file, ei->change->events);
8404
8405         putFile16BitBE(file, ei->change->target_element);
8406
8407         putFile16BitBE(file, ei->change->delay_fixed);
8408         putFile16BitBE(file, ei->change->delay_random);
8409         putFile16BitBE(file, ei->change->delay_frames);
8410
8411         putFile16BitBE(file, ei->change->initial_trigger_element);
8412
8413         putFile8Bit(file, ei->change->explode);
8414         putFile8Bit(file, ei->change->use_target_content);
8415         putFile8Bit(file, ei->change->only_if_complete);
8416         putFile8Bit(file, ei->change->use_random_replace);
8417
8418         putFile8Bit(file, ei->change->random_percentage);
8419         putFile8Bit(file, ei->change->replace_when);
8420
8421         for (y = 0; y < 3; y++)
8422           for (x = 0; x < 3; x++)
8423             putFile16BitBE(file, ei->change->content.e[x][y]);
8424
8425         putFile8Bit(file, ei->slippery_type);
8426
8427         // some free bytes for future properties and padding
8428         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8429       }
8430
8431       check++;
8432     }
8433   }
8434
8435   if (check != num_changed_custom_elements)     // should not happen
8436     Warn("inconsistent number of custom element properties");
8437 }
8438 #endif
8439
8440 #if ENABLE_HISTORIC_CHUNKS
8441 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8442 {
8443   struct ElementInfo *ei = &element_info[element];
8444   int i, j, x, y;
8445
8446   // ---------- custom element base property values (96 bytes) ----------------
8447
8448   putFile16BitBE(file, element);
8449
8450   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8451     putFile8Bit(file, ei->description[i]);
8452
8453   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8454
8455   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8456
8457   putFile8Bit(file, ei->num_change_pages);
8458
8459   putFile16BitBE(file, ei->ce_value_fixed_initial);
8460   putFile16BitBE(file, ei->ce_value_random_initial);
8461   putFile8Bit(file, ei->use_last_ce_value);
8462
8463   putFile8Bit(file, ei->use_gfx_element);
8464   putFile16BitBE(file, ei->gfx_element_initial);
8465
8466   putFile8Bit(file, ei->collect_score_initial);
8467   putFile8Bit(file, ei->collect_count_initial);
8468
8469   putFile8Bit(file, ei->drop_delay_fixed);
8470   putFile8Bit(file, ei->push_delay_fixed);
8471   putFile8Bit(file, ei->drop_delay_random);
8472   putFile8Bit(file, ei->push_delay_random);
8473   putFile16BitBE(file, ei->move_delay_fixed);
8474   putFile16BitBE(file, ei->move_delay_random);
8475
8476   // bits 0 - 15 of "move_pattern" ...
8477   putFile16BitBE(file, ei->move_pattern & 0xffff);
8478   putFile8Bit(file, ei->move_direction_initial);
8479   putFile8Bit(file, ei->move_stepsize);
8480
8481   putFile8Bit(file, ei->slippery_type);
8482
8483   for (y = 0; y < 3; y++)
8484     for (x = 0; x < 3; x++)
8485       putFile16BitBE(file, ei->content.e[x][y]);
8486
8487   putFile16BitBE(file, ei->move_enter_element);
8488   putFile16BitBE(file, ei->move_leave_element);
8489   putFile8Bit(file, ei->move_leave_type);
8490
8491   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8492   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8493
8494   putFile8Bit(file, ei->access_direction);
8495
8496   putFile8Bit(file, ei->explosion_delay);
8497   putFile8Bit(file, ei->ignition_delay);
8498   putFile8Bit(file, ei->explosion_type);
8499
8500   // some free bytes for future custom property values and padding
8501   WriteUnusedBytesToFile(file, 1);
8502
8503   // ---------- change page property values (48 bytes) ------------------------
8504
8505   for (i = 0; i < ei->num_change_pages; i++)
8506   {
8507     struct ElementChangeInfo *change = &ei->change_page[i];
8508     unsigned int event_bits;
8509
8510     // bits 0 - 31 of "has_event[]" ...
8511     event_bits = 0;
8512     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8513       if (change->has_event[j])
8514         event_bits |= (1u << j);
8515     putFile32BitBE(file, event_bits);
8516
8517     putFile16BitBE(file, change->target_element);
8518
8519     putFile16BitBE(file, change->delay_fixed);
8520     putFile16BitBE(file, change->delay_random);
8521     putFile16BitBE(file, change->delay_frames);
8522
8523     putFile16BitBE(file, change->initial_trigger_element);
8524
8525     putFile8Bit(file, change->explode);
8526     putFile8Bit(file, change->use_target_content);
8527     putFile8Bit(file, change->only_if_complete);
8528     putFile8Bit(file, change->use_random_replace);
8529
8530     putFile8Bit(file, change->random_percentage);
8531     putFile8Bit(file, change->replace_when);
8532
8533     for (y = 0; y < 3; y++)
8534       for (x = 0; x < 3; x++)
8535         putFile16BitBE(file, change->target_content.e[x][y]);
8536
8537     putFile8Bit(file, change->can_change);
8538
8539     putFile8Bit(file, change->trigger_side);
8540
8541     putFile8Bit(file, change->trigger_player);
8542     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8543                        log_2(change->trigger_page)));
8544
8545     putFile8Bit(file, change->has_action);
8546     putFile8Bit(file, change->action_type);
8547     putFile8Bit(file, change->action_mode);
8548     putFile16BitBE(file, change->action_arg);
8549
8550     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8551     event_bits = 0;
8552     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8553       if (change->has_event[j])
8554         event_bits |= (1u << (j - 32));
8555     putFile8Bit(file, event_bits);
8556   }
8557 }
8558 #endif
8559
8560 #if ENABLE_HISTORIC_CHUNKS
8561 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8562 {
8563   struct ElementInfo *ei = &element_info[element];
8564   struct ElementGroupInfo *group = ei->group;
8565   int i;
8566
8567   putFile16BitBE(file, element);
8568
8569   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8570     putFile8Bit(file, ei->description[i]);
8571
8572   putFile8Bit(file, group->num_elements);
8573
8574   putFile8Bit(file, ei->use_gfx_element);
8575   putFile16BitBE(file, ei->gfx_element_initial);
8576
8577   putFile8Bit(file, group->choice_mode);
8578
8579   // some free bytes for future values and padding
8580   WriteUnusedBytesToFile(file, 3);
8581
8582   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8583     putFile16BitBE(file, group->element[i]);
8584 }
8585 #endif
8586
8587 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8588                                 boolean write_element)
8589 {
8590   int save_type = entry->save_type;
8591   int data_type = entry->data_type;
8592   int conf_type = entry->conf_type;
8593   int byte_mask = conf_type & CONF_MASK_BYTES;
8594   int element = entry->element;
8595   int default_value = entry->default_value;
8596   int num_bytes = 0;
8597   boolean modified = FALSE;
8598
8599   if (byte_mask != CONF_MASK_MULTI_BYTES)
8600   {
8601     void *value_ptr = entry->value;
8602     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8603                  *(int *)value_ptr);
8604
8605     // check if any settings have been modified before saving them
8606     if (value != default_value)
8607       modified = TRUE;
8608
8609     // do not save if explicitly told or if unmodified default settings
8610     if ((save_type == SAVE_CONF_NEVER) ||
8611         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8612       return 0;
8613
8614     if (write_element)
8615       num_bytes += putFile16BitBE(file, element);
8616
8617     num_bytes += putFile8Bit(file, conf_type);
8618     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8619                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8620                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8621                   0);
8622   }
8623   else if (data_type == TYPE_STRING)
8624   {
8625     char *default_string = entry->default_string;
8626     char *string = (char *)(entry->value);
8627     int string_length = strlen(string);
8628     int i;
8629
8630     // check if any settings have been modified before saving them
8631     if (!strEqual(string, default_string))
8632       modified = TRUE;
8633
8634     // do not save if explicitly told or if unmodified default settings
8635     if ((save_type == SAVE_CONF_NEVER) ||
8636         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8637       return 0;
8638
8639     if (write_element)
8640       num_bytes += putFile16BitBE(file, element);
8641
8642     num_bytes += putFile8Bit(file, conf_type);
8643     num_bytes += putFile16BitBE(file, string_length);
8644
8645     for (i = 0; i < string_length; i++)
8646       num_bytes += putFile8Bit(file, string[i]);
8647   }
8648   else if (data_type == TYPE_ELEMENT_LIST)
8649   {
8650     int *element_array = (int *)(entry->value);
8651     int num_elements = *(int *)(entry->num_entities);
8652     int i;
8653
8654     // check if any settings have been modified before saving them
8655     for (i = 0; i < num_elements; i++)
8656       if (element_array[i] != default_value)
8657         modified = TRUE;
8658
8659     // do not save if explicitly told or if unmodified default settings
8660     if ((save_type == SAVE_CONF_NEVER) ||
8661         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8662       return 0;
8663
8664     if (write_element)
8665       num_bytes += putFile16BitBE(file, element);
8666
8667     num_bytes += putFile8Bit(file, conf_type);
8668     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8669
8670     for (i = 0; i < num_elements; i++)
8671       num_bytes += putFile16BitBE(file, element_array[i]);
8672   }
8673   else if (data_type == TYPE_CONTENT_LIST)
8674   {
8675     struct Content *content = (struct Content *)(entry->value);
8676     int num_contents = *(int *)(entry->num_entities);
8677     int i, x, y;
8678
8679     // check if any settings have been modified before saving them
8680     for (i = 0; i < num_contents; i++)
8681       for (y = 0; y < 3; y++)
8682         for (x = 0; x < 3; x++)
8683           if (content[i].e[x][y] != default_value)
8684             modified = TRUE;
8685
8686     // do not save if explicitly told or if unmodified default settings
8687     if ((save_type == SAVE_CONF_NEVER) ||
8688         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8689       return 0;
8690
8691     if (write_element)
8692       num_bytes += putFile16BitBE(file, element);
8693
8694     num_bytes += putFile8Bit(file, conf_type);
8695     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8696
8697     for (i = 0; i < num_contents; i++)
8698       for (y = 0; y < 3; y++)
8699         for (x = 0; x < 3; x++)
8700           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8701   }
8702
8703   return num_bytes;
8704 }
8705
8706 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8707 {
8708   int chunk_size = 0;
8709   int i;
8710
8711   li = *level;          // copy level data into temporary buffer
8712
8713   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8714     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8715
8716   return chunk_size;
8717 }
8718
8719 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8720 {
8721   int chunk_size = 0;
8722   int i;
8723
8724   li = *level;          // copy level data into temporary buffer
8725
8726   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8727     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8728
8729   return chunk_size;
8730 }
8731
8732 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8733 {
8734   int envelope_nr = element - EL_ENVELOPE_1;
8735   int chunk_size = 0;
8736   int i;
8737
8738   chunk_size += putFile16BitBE(file, element);
8739
8740   // copy envelope data into temporary buffer
8741   xx_envelope = level->envelope[envelope_nr];
8742
8743   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8744     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8745
8746   return chunk_size;
8747 }
8748
8749 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8750 {
8751   struct ElementInfo *ei = &element_info[element];
8752   int chunk_size = 0;
8753   int i, j;
8754
8755   chunk_size += putFile16BitBE(file, element);
8756
8757   xx_ei = *ei;          // copy element data into temporary buffer
8758
8759   // set default description string for this specific element
8760   strcpy(xx_default_description, getDefaultElementDescription(ei));
8761
8762   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8763     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8764
8765   for (i = 0; i < ei->num_change_pages; i++)
8766   {
8767     struct ElementChangeInfo *change = &ei->change_page[i];
8768
8769     xx_current_change_page = i;
8770
8771     xx_change = *change;        // copy change data into temporary buffer
8772
8773     resetEventBits();
8774     setEventBitsFromEventFlags(change);
8775
8776     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8777       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8778                                          FALSE);
8779   }
8780
8781   return chunk_size;
8782 }
8783
8784 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8785 {
8786   struct ElementInfo *ei = &element_info[element];
8787   struct ElementGroupInfo *group = ei->group;
8788   int chunk_size = 0;
8789   int i;
8790
8791   chunk_size += putFile16BitBE(file, element);
8792
8793   xx_ei = *ei;          // copy element data into temporary buffer
8794   xx_group = *group;    // copy group data into temporary buffer
8795
8796   // set default description string for this specific element
8797   strcpy(xx_default_description, getDefaultElementDescription(ei));
8798
8799   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8800     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8801
8802   return chunk_size;
8803 }
8804
8805 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8806 {
8807   struct ElementInfo *ei = &element_info[element];
8808   int chunk_size = 0;
8809   int i;
8810
8811   chunk_size += putFile16BitBE(file, element);
8812
8813   xx_ei = *ei;          // copy element data into temporary buffer
8814
8815   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8816     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8817
8818   return chunk_size;
8819 }
8820
8821 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8822                                   boolean save_as_template)
8823 {
8824   int chunk_size;
8825   int i;
8826   FILE *file;
8827
8828   if (!(file = fopen(filename, MODE_WRITE)))
8829   {
8830     Warn("cannot save level file '%s'", filename);
8831
8832     return;
8833   }
8834
8835   level->file_version = FILE_VERSION_ACTUAL;
8836   level->game_version = GAME_VERSION_ACTUAL;
8837
8838   level->creation_date = getCurrentDate();
8839
8840   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8841   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8842
8843   chunk_size = SaveLevel_VERS(NULL, level);
8844   putFileChunkBE(file, "VERS", chunk_size);
8845   SaveLevel_VERS(file, level);
8846
8847   chunk_size = SaveLevel_DATE(NULL, level);
8848   putFileChunkBE(file, "DATE", chunk_size);
8849   SaveLevel_DATE(file, level);
8850
8851   chunk_size = SaveLevel_NAME(NULL, level);
8852   putFileChunkBE(file, "NAME", chunk_size);
8853   SaveLevel_NAME(file, level);
8854
8855   chunk_size = SaveLevel_AUTH(NULL, level);
8856   putFileChunkBE(file, "AUTH", chunk_size);
8857   SaveLevel_AUTH(file, level);
8858
8859   chunk_size = SaveLevel_INFO(NULL, level);
8860   putFileChunkBE(file, "INFO", chunk_size);
8861   SaveLevel_INFO(file, level);
8862
8863   chunk_size = SaveLevel_BODY(NULL, level);
8864   putFileChunkBE(file, "BODY", chunk_size);
8865   SaveLevel_BODY(file, level);
8866
8867   chunk_size = SaveLevel_ELEM(NULL, level);
8868   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8869   {
8870     putFileChunkBE(file, "ELEM", chunk_size);
8871     SaveLevel_ELEM(file, level);
8872   }
8873
8874   for (i = 0; i < NUM_ENVELOPES; i++)
8875   {
8876     int element = EL_ENVELOPE_1 + i;
8877
8878     chunk_size = SaveLevel_NOTE(NULL, level, element);
8879     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8880     {
8881       putFileChunkBE(file, "NOTE", chunk_size);
8882       SaveLevel_NOTE(file, level, element);
8883     }
8884   }
8885
8886   // if not using template level, check for non-default custom/group elements
8887   if (!level->use_custom_template || save_as_template)
8888   {
8889     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8890     {
8891       int element = EL_CUSTOM_START + i;
8892
8893       chunk_size = SaveLevel_CUSX(NULL, level, element);
8894       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8895       {
8896         putFileChunkBE(file, "CUSX", chunk_size);
8897         SaveLevel_CUSX(file, level, element);
8898       }
8899     }
8900
8901     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8902     {
8903       int element = EL_GROUP_START + i;
8904
8905       chunk_size = SaveLevel_GRPX(NULL, level, element);
8906       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8907       {
8908         putFileChunkBE(file, "GRPX", chunk_size);
8909         SaveLevel_GRPX(file, level, element);
8910       }
8911     }
8912
8913     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8914     {
8915       int element = GET_EMPTY_ELEMENT(i);
8916
8917       chunk_size = SaveLevel_EMPX(NULL, level, element);
8918       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8919       {
8920         putFileChunkBE(file, "EMPX", chunk_size);
8921         SaveLevel_EMPX(file, level, element);
8922       }
8923     }
8924   }
8925
8926   fclose(file);
8927
8928   SetFilePermissions(filename, PERMS_PRIVATE);
8929 }
8930
8931 void SaveLevel(int nr)
8932 {
8933   char *filename = getDefaultLevelFilename(nr);
8934
8935   SaveLevelFromFilename(&level, filename, FALSE);
8936 }
8937
8938 void SaveLevelTemplate(void)
8939 {
8940   char *filename = getLocalLevelTemplateFilename();
8941
8942   SaveLevelFromFilename(&level, filename, TRUE);
8943 }
8944
8945 boolean SaveLevelChecked(int nr)
8946 {
8947   char *filename = getDefaultLevelFilename(nr);
8948   boolean new_level = !fileExists(filename);
8949   boolean level_saved = FALSE;
8950
8951   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8952   {
8953     SaveLevel(nr);
8954
8955     if (new_level)
8956       Request("Level saved!", REQ_CONFIRM);
8957
8958     level_saved = TRUE;
8959   }
8960
8961   return level_saved;
8962 }
8963
8964 void DumpLevel(struct LevelInfo *level)
8965 {
8966   if (level->no_level_file || level->no_valid_file)
8967   {
8968     Warn("cannot dump -- no valid level file found");
8969
8970     return;
8971   }
8972
8973   PrintLine("-", 79);
8974   Print("Level xxx (file version %08d, game version %08d)\n",
8975         level->file_version, level->game_version);
8976   PrintLine("-", 79);
8977
8978   Print("Level author: '%s'\n", level->author);
8979   Print("Level title:  '%s'\n", level->name);
8980   Print("\n");
8981   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8982   Print("\n");
8983   Print("Level time:  %d seconds\n", level->time);
8984   Print("Gems needed: %d\n", level->gems_needed);
8985   Print("\n");
8986   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8987   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8988   Print("Time for light:      %d seconds\n", level->time_light);
8989   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8990   Print("\n");
8991   Print("Amoeba speed: %d\n", level->amoeba_speed);
8992   Print("\n");
8993
8994   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8995   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8996   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8997   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8998   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8999   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9000
9001   if (options.debug)
9002   {
9003     int i, j;
9004
9005     for (i = 0; i < NUM_ENVELOPES; i++)
9006     {
9007       char *text = level->envelope[i].text;
9008       int text_len = strlen(text);
9009       boolean has_text = FALSE;
9010
9011       for (j = 0; j < text_len; j++)
9012         if (text[j] != ' ' && text[j] != '\n')
9013           has_text = TRUE;
9014
9015       if (has_text)
9016       {
9017         Print("\n");
9018         Print("Envelope %d:\n'%s'\n", i + 1, text);
9019       }
9020     }
9021   }
9022
9023   PrintLine("-", 79);
9024 }
9025
9026 void DumpLevels(void)
9027 {
9028   static LevelDirTree *dumplevel_leveldir = NULL;
9029
9030   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9031                                                  global.dumplevel_leveldir);
9032
9033   if (dumplevel_leveldir == NULL)
9034     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9035
9036   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9037       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9038     Fail("no such level number: %d", global.dumplevel_level_nr);
9039
9040   leveldir_current = dumplevel_leveldir;
9041
9042   LoadLevel(global.dumplevel_level_nr);
9043   DumpLevel(&level);
9044
9045   CloseAllAndExit(0);
9046 }
9047
9048 void DumpLevelsetFromFilename_BD(char *filename)
9049 {
9050   if (leveldir_current == NULL) // no levelsets loaded yet
9051     bd_open_all();
9052
9053   if (!LoadNativeLevel_BD(filename, 0, FALSE))
9054     CloseAllAndExit(0);         // function has already printed warning
9055
9056   PrintLine("-", 79);
9057   Print("Levelset '%s'\n", filename);
9058   PrintLine("-", 79);
9059
9060   DumpLevelset_BD();
9061
9062   PrintLine("-", 79);
9063
9064   CloseAllAndExit(0);
9065 }
9066
9067 void DumpLevelset(void)
9068 {
9069   static LevelDirTree *dumplevelset_leveldir = NULL;
9070
9071   dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9072                                                     global.dumplevelset_leveldir);
9073   if (dumplevelset_leveldir == NULL)
9074     Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9075
9076   PrintLine("-", 79);
9077   Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9078   PrintLine("-", 79);
9079
9080   Print("Number of levels:   %d\n", dumplevelset_leveldir->levels);
9081   Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9082
9083   PrintLine("-", 79);
9084
9085   CloseAllAndExit(0);
9086 }
9087
9088
9089 // ============================================================================
9090 // tape file functions
9091 // ============================================================================
9092
9093 static void setTapeInfoToDefaults(void)
9094 {
9095   int i;
9096
9097   // always start with reliable default values (empty tape)
9098   TapeErase();
9099
9100   // default values (also for pre-1.2 tapes) with only the first player
9101   tape.player_participates[0] = TRUE;
9102   for (i = 1; i < MAX_PLAYERS; i++)
9103     tape.player_participates[i] = FALSE;
9104
9105   // at least one (default: the first) player participates in every tape
9106   tape.num_participating_players = 1;
9107
9108   tape.property_bits = TAPE_PROPERTY_NONE;
9109
9110   tape.level_nr = level_nr;
9111   tape.counter = 0;
9112   tape.changed = FALSE;
9113   tape.solved = FALSE;
9114
9115   tape.recording = FALSE;
9116   tape.playing = FALSE;
9117   tape.pausing = FALSE;
9118
9119   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9120   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9121
9122   tape.no_info_chunk = TRUE;
9123   tape.no_valid_file = FALSE;
9124 }
9125
9126 static int getTapePosSize(struct TapeInfo *tape)
9127 {
9128   int tape_pos_size = 0;
9129
9130   if (tape->use_key_actions)
9131     tape_pos_size += tape->num_participating_players;
9132
9133   if (tape->use_mouse_actions)
9134     tape_pos_size += 3;         // x and y position and mouse button mask
9135
9136   tape_pos_size += 1;           // tape action delay value
9137
9138   return tape_pos_size;
9139 }
9140
9141 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9142 {
9143   tape->use_key_actions = FALSE;
9144   tape->use_mouse_actions = FALSE;
9145
9146   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9147     tape->use_key_actions = TRUE;
9148
9149   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9150     tape->use_mouse_actions = TRUE;
9151 }
9152
9153 static int getTapeActionValue(struct TapeInfo *tape)
9154 {
9155   return (tape->use_key_actions &&
9156           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9157           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9158           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9159           TAPE_ACTIONS_DEFAULT);
9160 }
9161
9162 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9163 {
9164   tape->file_version = getFileVersion(file);
9165   tape->game_version = getFileVersion(file);
9166
9167   return chunk_size;
9168 }
9169
9170 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9171 {
9172   int i;
9173
9174   tape->random_seed = getFile32BitBE(file);
9175   tape->date        = getFile32BitBE(file);
9176   tape->length      = getFile32BitBE(file);
9177
9178   // read header fields that are new since version 1.2
9179   if (tape->file_version >= FILE_VERSION_1_2)
9180   {
9181     byte store_participating_players = getFile8Bit(file);
9182     int engine_version;
9183
9184     // since version 1.2, tapes store which players participate in the tape
9185     tape->num_participating_players = 0;
9186     for (i = 0; i < MAX_PLAYERS; i++)
9187     {
9188       tape->player_participates[i] = FALSE;
9189
9190       if (store_participating_players & (1 << i))
9191       {
9192         tape->player_participates[i] = TRUE;
9193         tape->num_participating_players++;
9194       }
9195     }
9196
9197     setTapeActionFlags(tape, getFile8Bit(file));
9198
9199     tape->property_bits = getFile8Bit(file);
9200     tape->solved = getFile8Bit(file);
9201
9202     engine_version = getFileVersion(file);
9203     if (engine_version > 0)
9204       tape->engine_version = engine_version;
9205     else
9206       tape->engine_version = tape->game_version;
9207   }
9208
9209   return chunk_size;
9210 }
9211
9212 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9213 {
9214   tape->scr_fieldx = getFile8Bit(file);
9215   tape->scr_fieldy = getFile8Bit(file);
9216
9217   return chunk_size;
9218 }
9219
9220 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9221 {
9222   char *level_identifier = NULL;
9223   int level_identifier_size;
9224   int i;
9225
9226   tape->no_info_chunk = FALSE;
9227
9228   level_identifier_size = getFile16BitBE(file);
9229
9230   level_identifier = checked_malloc(level_identifier_size);
9231
9232   for (i = 0; i < level_identifier_size; i++)
9233     level_identifier[i] = getFile8Bit(file);
9234
9235   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9236   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9237
9238   checked_free(level_identifier);
9239
9240   tape->level_nr = getFile16BitBE(file);
9241
9242   chunk_size = 2 + level_identifier_size + 2;
9243
9244   return chunk_size;
9245 }
9246
9247 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9248 {
9249   int i, j;
9250   int tape_pos_size = getTapePosSize(tape);
9251   int chunk_size_expected = tape_pos_size * tape->length;
9252
9253   if (chunk_size_expected != chunk_size)
9254   {
9255     ReadUnusedBytesFromFile(file, chunk_size);
9256     return chunk_size_expected;
9257   }
9258
9259   for (i = 0; i < tape->length; i++)
9260   {
9261     if (i >= MAX_TAPE_LEN)
9262     {
9263       Warn("tape truncated -- size exceeds maximum tape size %d",
9264             MAX_TAPE_LEN);
9265
9266       // tape too large; read and ignore remaining tape data from this chunk
9267       for (;i < tape->length; i++)
9268         ReadUnusedBytesFromFile(file, tape_pos_size);
9269
9270       break;
9271     }
9272
9273     if (tape->use_key_actions)
9274     {
9275       for (j = 0; j < MAX_PLAYERS; j++)
9276       {
9277         tape->pos[i].action[j] = MV_NONE;
9278
9279         if (tape->player_participates[j])
9280           tape->pos[i].action[j] = getFile8Bit(file);
9281       }
9282     }
9283
9284     if (tape->use_mouse_actions)
9285     {
9286       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9287       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9288       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9289     }
9290
9291     tape->pos[i].delay = getFile8Bit(file);
9292
9293     if (tape->file_version == FILE_VERSION_1_0)
9294     {
9295       // eliminate possible diagonal moves in old tapes
9296       // this is only for backward compatibility
9297
9298       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9299       byte action = tape->pos[i].action[0];
9300       int k, num_moves = 0;
9301
9302       for (k = 0; k < 4; k++)
9303       {
9304         if (action & joy_dir[k])
9305         {
9306           tape->pos[i + num_moves].action[0] = joy_dir[k];
9307           if (num_moves > 0)
9308             tape->pos[i + num_moves].delay = 0;
9309           num_moves++;
9310         }
9311       }
9312
9313       if (num_moves > 1)
9314       {
9315         num_moves--;
9316         i += num_moves;
9317         tape->length += num_moves;
9318       }
9319     }
9320     else if (tape->file_version < FILE_VERSION_2_0)
9321     {
9322       // convert pre-2.0 tapes to new tape format
9323
9324       if (tape->pos[i].delay > 1)
9325       {
9326         // action part
9327         tape->pos[i + 1] = tape->pos[i];
9328         tape->pos[i + 1].delay = 1;
9329
9330         // delay part
9331         for (j = 0; j < MAX_PLAYERS; j++)
9332           tape->pos[i].action[j] = MV_NONE;
9333         tape->pos[i].delay--;
9334
9335         i++;
9336         tape->length++;
9337       }
9338     }
9339
9340     if (checkEndOfFile(file))
9341       break;
9342   }
9343
9344   if (i != tape->length)
9345     chunk_size = tape_pos_size * i;
9346
9347   return chunk_size;
9348 }
9349
9350 static void LoadTape_SokobanSolution(char *filename)
9351 {
9352   File *file;
9353   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9354
9355   if (!(file = openFile(filename, MODE_READ)))
9356   {
9357     tape.no_valid_file = TRUE;
9358
9359     return;
9360   }
9361
9362   while (!checkEndOfFile(file))
9363   {
9364     unsigned char c = getByteFromFile(file);
9365
9366     if (checkEndOfFile(file))
9367       break;
9368
9369     switch (c)
9370     {
9371       case 'u':
9372       case 'U':
9373         tape.pos[tape.length].action[0] = MV_UP;
9374         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9375         tape.length++;
9376         break;
9377
9378       case 'd':
9379       case 'D':
9380         tape.pos[tape.length].action[0] = MV_DOWN;
9381         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9382         tape.length++;
9383         break;
9384
9385       case 'l':
9386       case 'L':
9387         tape.pos[tape.length].action[0] = MV_LEFT;
9388         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9389         tape.length++;
9390         break;
9391
9392       case 'r':
9393       case 'R':
9394         tape.pos[tape.length].action[0] = MV_RIGHT;
9395         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9396         tape.length++;
9397         break;
9398
9399       case '\n':
9400       case '\r':
9401       case '\t':
9402       case ' ':
9403         // ignore white-space characters
9404         break;
9405
9406       default:
9407         tape.no_valid_file = TRUE;
9408
9409         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9410
9411         break;
9412     }
9413   }
9414
9415   closeFile(file);
9416
9417   if (tape.no_valid_file)
9418     return;
9419
9420   tape.length_frames  = GetTapeLengthFrames();
9421   tape.length_seconds = GetTapeLengthSeconds();
9422 }
9423
9424 void LoadTapeFromFilename(char *filename)
9425 {
9426   char cookie[MAX_LINE_LEN];
9427   char chunk_name[CHUNK_ID_LEN + 1];
9428   File *file;
9429   int chunk_size;
9430
9431   // always start with reliable default values
9432   setTapeInfoToDefaults();
9433
9434   if (strSuffix(filename, ".sln"))
9435   {
9436     LoadTape_SokobanSolution(filename);
9437
9438     return;
9439   }
9440
9441   if (!(file = openFile(filename, MODE_READ)))
9442   {
9443     tape.no_valid_file = TRUE;
9444
9445     return;
9446   }
9447
9448   getFileChunkBE(file, chunk_name, NULL);
9449   if (strEqual(chunk_name, "RND1"))
9450   {
9451     getFile32BitBE(file);               // not used
9452
9453     getFileChunkBE(file, chunk_name, NULL);
9454     if (!strEqual(chunk_name, "TAPE"))
9455     {
9456       tape.no_valid_file = TRUE;
9457
9458       Warn("unknown format of tape file '%s'", filename);
9459
9460       closeFile(file);
9461
9462       return;
9463     }
9464   }
9465   else  // check for pre-2.0 file format with cookie string
9466   {
9467     strcpy(cookie, chunk_name);
9468     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9469       cookie[4] = '\0';
9470     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9471       cookie[strlen(cookie) - 1] = '\0';
9472
9473     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9474     {
9475       tape.no_valid_file = TRUE;
9476
9477       Warn("unknown format of tape file '%s'", filename);
9478
9479       closeFile(file);
9480
9481       return;
9482     }
9483
9484     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9485     {
9486       tape.no_valid_file = TRUE;
9487
9488       Warn("unsupported version of tape file '%s'", filename);
9489
9490       closeFile(file);
9491
9492       return;
9493     }
9494
9495     // pre-2.0 tape files have no game version, so use file version here
9496     tape.game_version = tape.file_version;
9497   }
9498
9499   if (tape.file_version < FILE_VERSION_1_2)
9500   {
9501     // tape files from versions before 1.2.0 without chunk structure
9502     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9503     LoadTape_BODY(file, 2 * tape.length,      &tape);
9504   }
9505   else
9506   {
9507     static struct
9508     {
9509       char *name;
9510       int size;
9511       int (*loader)(File *, int, struct TapeInfo *);
9512     }
9513     chunk_info[] =
9514     {
9515       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9516       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9517       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9518       { "INFO", -1,                     LoadTape_INFO },
9519       { "BODY", -1,                     LoadTape_BODY },
9520       {  NULL,  0,                      NULL }
9521     };
9522
9523     while (getFileChunkBE(file, chunk_name, &chunk_size))
9524     {
9525       int i = 0;
9526
9527       while (chunk_info[i].name != NULL &&
9528              !strEqual(chunk_name, chunk_info[i].name))
9529         i++;
9530
9531       if (chunk_info[i].name == NULL)
9532       {
9533         Warn("unknown chunk '%s' in tape file '%s'",
9534               chunk_name, filename);
9535
9536         ReadUnusedBytesFromFile(file, chunk_size);
9537       }
9538       else if (chunk_info[i].size != -1 &&
9539                chunk_info[i].size != chunk_size)
9540       {
9541         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9542               chunk_size, chunk_name, filename);
9543
9544         ReadUnusedBytesFromFile(file, chunk_size);
9545       }
9546       else
9547       {
9548         // call function to load this tape chunk
9549         int chunk_size_expected =
9550           (chunk_info[i].loader)(file, chunk_size, &tape);
9551
9552         // the size of some chunks cannot be checked before reading other
9553         // chunks first (like "HEAD" and "BODY") that contain some header
9554         // information, so check them here
9555         if (chunk_size_expected != chunk_size)
9556         {
9557           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9558                 chunk_size, chunk_name, filename);
9559         }
9560       }
9561     }
9562   }
9563
9564   closeFile(file);
9565
9566   tape.length_frames  = GetTapeLengthFrames();
9567   tape.length_seconds = GetTapeLengthSeconds();
9568
9569 #if 0
9570   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9571         tape.file_version);
9572   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9573         tape.game_version);
9574   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9575         tape.engine_version);
9576 #endif
9577 }
9578
9579 void LoadTape(int nr)
9580 {
9581   char *filename = getTapeFilename(nr);
9582
9583   LoadTapeFromFilename(filename);
9584 }
9585
9586 void LoadSolutionTape(int nr)
9587 {
9588   char *filename = getSolutionTapeFilename(nr);
9589
9590   LoadTapeFromFilename(filename);
9591
9592   if (TAPE_IS_EMPTY(tape))
9593   {
9594     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9595         level.native_bd_level->replay != NULL)
9596       CopyNativeTape_BD_to_RND(&level);
9597     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9598         level.native_sp_level->demo.is_available)
9599       CopyNativeTape_SP_to_RND(&level);
9600   }
9601 }
9602
9603 void LoadScoreTape(char *score_tape_basename, int nr)
9604 {
9605   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9606
9607   LoadTapeFromFilename(filename);
9608 }
9609
9610 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9611 {
9612   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9613
9614   LoadTapeFromFilename(filename);
9615 }
9616
9617 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9618 {
9619   // chunk required for team mode tapes with non-default screen size
9620   return (tape->num_participating_players > 1 &&
9621           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9622            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9623 }
9624
9625 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9626 {
9627   putFileVersion(file, tape->file_version);
9628   putFileVersion(file, tape->game_version);
9629 }
9630
9631 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9632 {
9633   int i;
9634   byte store_participating_players = 0;
9635
9636   // set bits for participating players for compact storage
9637   for (i = 0; i < MAX_PLAYERS; i++)
9638     if (tape->player_participates[i])
9639       store_participating_players |= (1 << i);
9640
9641   putFile32BitBE(file, tape->random_seed);
9642   putFile32BitBE(file, tape->date);
9643   putFile32BitBE(file, tape->length);
9644
9645   putFile8Bit(file, store_participating_players);
9646
9647   putFile8Bit(file, getTapeActionValue(tape));
9648
9649   putFile8Bit(file, tape->property_bits);
9650   putFile8Bit(file, tape->solved);
9651
9652   putFileVersion(file, tape->engine_version);
9653 }
9654
9655 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9656 {
9657   putFile8Bit(file, tape->scr_fieldx);
9658   putFile8Bit(file, tape->scr_fieldy);
9659 }
9660
9661 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9662 {
9663   int level_identifier_size = strlen(tape->level_identifier) + 1;
9664   int i;
9665
9666   putFile16BitBE(file, level_identifier_size);
9667
9668   for (i = 0; i < level_identifier_size; i++)
9669     putFile8Bit(file, tape->level_identifier[i]);
9670
9671   putFile16BitBE(file, tape->level_nr);
9672 }
9673
9674 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9675 {
9676   int i, j;
9677
9678   for (i = 0; i < tape->length; i++)
9679   {
9680     if (tape->use_key_actions)
9681     {
9682       for (j = 0; j < MAX_PLAYERS; j++)
9683         if (tape->player_participates[j])
9684           putFile8Bit(file, tape->pos[i].action[j]);
9685     }
9686
9687     if (tape->use_mouse_actions)
9688     {
9689       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9690       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9691       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9692     }
9693
9694     putFile8Bit(file, tape->pos[i].delay);
9695   }
9696 }
9697
9698 void SaveTapeToFilename(char *filename)
9699 {
9700   FILE *file;
9701   int tape_pos_size;
9702   int info_chunk_size;
9703   int body_chunk_size;
9704
9705   if (!(file = fopen(filename, MODE_WRITE)))
9706   {
9707     Warn("cannot save level recording file '%s'", filename);
9708
9709     return;
9710   }
9711
9712   tape_pos_size = getTapePosSize(&tape);
9713
9714   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9715   body_chunk_size = tape_pos_size * tape.length;
9716
9717   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9718   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9719
9720   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9721   SaveTape_VERS(file, &tape);
9722
9723   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9724   SaveTape_HEAD(file, &tape);
9725
9726   if (checkSaveTape_SCRN(&tape))
9727   {
9728     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9729     SaveTape_SCRN(file, &tape);
9730   }
9731
9732   putFileChunkBE(file, "INFO", info_chunk_size);
9733   SaveTape_INFO(file, &tape);
9734
9735   putFileChunkBE(file, "BODY", body_chunk_size);
9736   SaveTape_BODY(file, &tape);
9737
9738   fclose(file);
9739
9740   SetFilePermissions(filename, PERMS_PRIVATE);
9741 }
9742
9743 static void SaveTapeExt(char *filename)
9744 {
9745   int i;
9746
9747   tape.file_version = FILE_VERSION_ACTUAL;
9748   tape.game_version = GAME_VERSION_ACTUAL;
9749
9750   tape.num_participating_players = 0;
9751
9752   // count number of participating players
9753   for (i = 0; i < MAX_PLAYERS; i++)
9754     if (tape.player_participates[i])
9755       tape.num_participating_players++;
9756
9757   SaveTapeToFilename(filename);
9758
9759   tape.changed = FALSE;
9760 }
9761
9762 void SaveTape(int nr)
9763 {
9764   char *filename = getTapeFilename(nr);
9765
9766   InitTapeDirectory(leveldir_current->subdir);
9767
9768   SaveTapeExt(filename);
9769 }
9770
9771 void SaveScoreTape(int nr)
9772 {
9773   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9774
9775   // used instead of "leveldir_current->subdir" (for network games)
9776   InitScoreTapeDirectory(levelset.identifier, nr);
9777
9778   SaveTapeExt(filename);
9779 }
9780
9781 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9782                                   unsigned int req_state_added)
9783 {
9784   char *filename = getTapeFilename(nr);
9785   boolean new_tape = !fileExists(filename);
9786   boolean tape_saved = FALSE;
9787
9788   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9789   {
9790     SaveTape(nr);
9791
9792     if (new_tape)
9793       Request(msg_saved, REQ_CONFIRM | req_state_added);
9794
9795     tape_saved = TRUE;
9796   }
9797
9798   return tape_saved;
9799 }
9800
9801 boolean SaveTapeChecked(int nr)
9802 {
9803   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9804 }
9805
9806 boolean SaveTapeChecked_LevelSolved(int nr)
9807 {
9808   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9809                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9810 }
9811
9812 void DumpTape(struct TapeInfo *tape)
9813 {
9814   int tape_frame_counter;
9815   int i, j;
9816
9817   if (tape->no_valid_file)
9818   {
9819     Warn("cannot dump -- no valid tape file found");
9820
9821     return;
9822   }
9823
9824   PrintLine("-", 79);
9825
9826   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9827         tape->level_nr, tape->file_version, tape->game_version);
9828   Print("                  (effective engine version %08d)\n",
9829         tape->engine_version);
9830   Print("Level series identifier: '%s'\n", tape->level_identifier);
9831
9832   Print("Solution tape: %s\n",
9833         tape->solved ? "yes" :
9834         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9835
9836   Print("Special tape properties: ");
9837   if (tape->property_bits == TAPE_PROPERTY_NONE)
9838     Print("[none]");
9839   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9840     Print("[em_random_bug]");
9841   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9842     Print("[game_speed]");
9843   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9844     Print("[pause]");
9845   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9846     Print("[single_step]");
9847   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9848     Print("[snapshot]");
9849   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9850     Print("[replayed]");
9851   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9852     Print("[tas_keys]");
9853   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9854     Print("[small_graphics]");
9855   Print("\n");
9856
9857   int year2 = tape->date / 10000;
9858   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9859   int month_index_raw = (tape->date / 100) % 100;
9860   int month_index = month_index_raw % 12;       // prevent invalid index
9861   int month = month_index + 1;
9862   int day = tape->date % 100;
9863
9864   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9865
9866   PrintLine("-", 79);
9867
9868   tape_frame_counter = 0;
9869
9870   for (i = 0; i < tape->length; i++)
9871   {
9872     if (i >= MAX_TAPE_LEN)
9873       break;
9874
9875     Print("%04d: ", i);
9876
9877     for (j = 0; j < MAX_PLAYERS; j++)
9878     {
9879       if (tape->player_participates[j])
9880       {
9881         int action = tape->pos[i].action[j];
9882
9883         Print("%d:%02x ", j, action);
9884         Print("[%c%c%c%c|%c%c] - ",
9885               (action & JOY_LEFT ? '<' : ' '),
9886               (action & JOY_RIGHT ? '>' : ' '),
9887               (action & JOY_UP ? '^' : ' '),
9888               (action & JOY_DOWN ? 'v' : ' '),
9889               (action & JOY_BUTTON_1 ? '1' : ' '),
9890               (action & JOY_BUTTON_2 ? '2' : ' '));
9891       }
9892     }
9893
9894     Print("(%03d) ", tape->pos[i].delay);
9895     Print("[%05d]\n", tape_frame_counter);
9896
9897     tape_frame_counter += tape->pos[i].delay;
9898   }
9899
9900   PrintLine("-", 79);
9901 }
9902
9903 void DumpTapes(void)
9904 {
9905   static LevelDirTree *dumptape_leveldir = NULL;
9906
9907   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9908                                                 global.dumptape_leveldir);
9909
9910   if (dumptape_leveldir == NULL)
9911     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9912
9913   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9914       global.dumptape_level_nr > dumptape_leveldir->last_level)
9915     Fail("no such level number: %d", global.dumptape_level_nr);
9916
9917   leveldir_current = dumptape_leveldir;
9918
9919   if (options.mytapes)
9920     LoadTape(global.dumptape_level_nr);
9921   else
9922     LoadSolutionTape(global.dumptape_level_nr);
9923
9924   DumpTape(&tape);
9925
9926   CloseAllAndExit(0);
9927 }
9928
9929
9930 // ============================================================================
9931 // score file functions
9932 // ============================================================================
9933
9934 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9935 {
9936   int i;
9937
9938   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9939   {
9940     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9941     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9942     scores->entry[i].score = 0;
9943     scores->entry[i].time = 0;
9944
9945     scores->entry[i].id = -1;
9946     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9947     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9948     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9949     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9950     strcpy(scores->entry[i].country_code, "??");
9951   }
9952
9953   scores->num_entries = 0;
9954   scores->last_added = -1;
9955   scores->last_added_local = -1;
9956
9957   scores->updated = FALSE;
9958   scores->uploaded = FALSE;
9959   scores->tape_downloaded = FALSE;
9960   scores->force_last_added = FALSE;
9961
9962   // The following values are intentionally not reset here:
9963   // - last_level_nr
9964   // - last_entry_nr
9965   // - next_level_nr
9966   // - continue_playing
9967   // - continue_on_return
9968 }
9969
9970 static void setScoreInfoToDefaults(void)
9971 {
9972   setScoreInfoToDefaultsExt(&scores);
9973 }
9974
9975 static void setServerScoreInfoToDefaults(void)
9976 {
9977   setScoreInfoToDefaultsExt(&server_scores);
9978 }
9979
9980 static void LoadScore_OLD(int nr)
9981 {
9982   int i;
9983   char *filename = getScoreFilename(nr);
9984   char cookie[MAX_LINE_LEN];
9985   char line[MAX_LINE_LEN];
9986   char *line_ptr;
9987   FILE *file;
9988
9989   if (!(file = fopen(filename, MODE_READ)))
9990     return;
9991
9992   // check file identifier
9993   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9994     cookie[0] = '\0';
9995   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9996     cookie[strlen(cookie) - 1] = '\0';
9997
9998   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9999   {
10000     Warn("unknown format of score file '%s'", filename);
10001
10002     fclose(file);
10003
10004     return;
10005   }
10006
10007   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10008   {
10009     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10010       Warn("fscanf() failed; %s", strerror(errno));
10011
10012     if (fgets(line, MAX_LINE_LEN, file) == NULL)
10013       line[0] = '\0';
10014
10015     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10016       line[strlen(line) - 1] = '\0';
10017
10018     for (line_ptr = line; *line_ptr; line_ptr++)
10019     {
10020       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10021       {
10022         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10023         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10024         break;
10025       }
10026     }
10027   }
10028
10029   fclose(file);
10030 }
10031
10032 static void ConvertScore_OLD(void)
10033 {
10034   // only convert score to time for levels that rate playing time over score
10035   if (!level.rate_time_over_score)
10036     return;
10037
10038   // convert old score to playing time for score-less levels (like Supaplex)
10039   int time_final_max = 999;
10040   int i;
10041
10042   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10043   {
10044     int score = scores.entry[i].score;
10045
10046     if (score > 0 && score < time_final_max)
10047       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10048   }
10049 }
10050
10051 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10052 {
10053   scores->file_version = getFileVersion(file);
10054   scores->game_version = getFileVersion(file);
10055
10056   return chunk_size;
10057 }
10058
10059 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10060 {
10061   char *level_identifier = NULL;
10062   int level_identifier_size;
10063   int i;
10064
10065   level_identifier_size = getFile16BitBE(file);
10066
10067   level_identifier = checked_malloc(level_identifier_size);
10068
10069   for (i = 0; i < level_identifier_size; i++)
10070     level_identifier[i] = getFile8Bit(file);
10071
10072   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10073   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10074
10075   checked_free(level_identifier);
10076
10077   scores->level_nr = getFile16BitBE(file);
10078   scores->num_entries = getFile16BitBE(file);
10079
10080   chunk_size = 2 + level_identifier_size + 2 + 2;
10081
10082   return chunk_size;
10083 }
10084
10085 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10086 {
10087   int i, j;
10088
10089   for (i = 0; i < scores->num_entries; i++)
10090   {
10091     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10092       scores->entry[i].name[j] = getFile8Bit(file);
10093
10094     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10095   }
10096
10097   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10098
10099   return chunk_size;
10100 }
10101
10102 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10103 {
10104   int i;
10105
10106   for (i = 0; i < scores->num_entries; i++)
10107     scores->entry[i].score = getFile16BitBE(file);
10108
10109   chunk_size = scores->num_entries * 2;
10110
10111   return chunk_size;
10112 }
10113
10114 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10115 {
10116   int i;
10117
10118   for (i = 0; i < scores->num_entries; i++)
10119     scores->entry[i].score = getFile32BitBE(file);
10120
10121   chunk_size = scores->num_entries * 4;
10122
10123   return chunk_size;
10124 }
10125
10126 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10127 {
10128   int i;
10129
10130   for (i = 0; i < scores->num_entries; i++)
10131     scores->entry[i].time = getFile32BitBE(file);
10132
10133   chunk_size = scores->num_entries * 4;
10134
10135   return chunk_size;
10136 }
10137
10138 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10139 {
10140   int i, j;
10141
10142   for (i = 0; i < scores->num_entries; i++)
10143   {
10144     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10145       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10146
10147     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10148   }
10149
10150   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10151
10152   return chunk_size;
10153 }
10154
10155 void LoadScore(int nr)
10156 {
10157   char *filename = getScoreFilename(nr);
10158   char cookie[MAX_LINE_LEN];
10159   char chunk_name[CHUNK_ID_LEN + 1];
10160   int chunk_size;
10161   boolean old_score_file_format = FALSE;
10162   File *file;
10163
10164   // always start with reliable default values
10165   setScoreInfoToDefaults();
10166
10167   if (!(file = openFile(filename, MODE_READ)))
10168     return;
10169
10170   getFileChunkBE(file, chunk_name, NULL);
10171   if (strEqual(chunk_name, "RND1"))
10172   {
10173     getFile32BitBE(file);               // not used
10174
10175     getFileChunkBE(file, chunk_name, NULL);
10176     if (!strEqual(chunk_name, "SCOR"))
10177     {
10178       Warn("unknown format of score file '%s'", filename);
10179
10180       closeFile(file);
10181
10182       return;
10183     }
10184   }
10185   else  // check for old file format with cookie string
10186   {
10187     strcpy(cookie, chunk_name);
10188     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10189       cookie[4] = '\0';
10190     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10191       cookie[strlen(cookie) - 1] = '\0';
10192
10193     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10194     {
10195       Warn("unknown format of score file '%s'", filename);
10196
10197       closeFile(file);
10198
10199       return;
10200     }
10201
10202     old_score_file_format = TRUE;
10203   }
10204
10205   if (old_score_file_format)
10206   {
10207     // score files from versions before 4.2.4.0 without chunk structure
10208     LoadScore_OLD(nr);
10209
10210     // convert score to time, if possible (mainly for Supaplex levels)
10211     ConvertScore_OLD();
10212   }
10213   else
10214   {
10215     static struct
10216     {
10217       char *name;
10218       int size;
10219       int (*loader)(File *, int, struct ScoreInfo *);
10220     }
10221     chunk_info[] =
10222     {
10223       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10224       { "INFO", -1,                     LoadScore_INFO },
10225       { "NAME", -1,                     LoadScore_NAME },
10226       { "SCOR", -1,                     LoadScore_SCOR },
10227       { "SC4R", -1,                     LoadScore_SC4R },
10228       { "TIME", -1,                     LoadScore_TIME },
10229       { "TAPE", -1,                     LoadScore_TAPE },
10230
10231       {  NULL,  0,                      NULL }
10232     };
10233
10234     while (getFileChunkBE(file, chunk_name, &chunk_size))
10235     {
10236       int i = 0;
10237
10238       while (chunk_info[i].name != NULL &&
10239              !strEqual(chunk_name, chunk_info[i].name))
10240         i++;
10241
10242       if (chunk_info[i].name == NULL)
10243       {
10244         Warn("unknown chunk '%s' in score file '%s'",
10245               chunk_name, filename);
10246
10247         ReadUnusedBytesFromFile(file, chunk_size);
10248       }
10249       else if (chunk_info[i].size != -1 &&
10250                chunk_info[i].size != chunk_size)
10251       {
10252         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10253               chunk_size, chunk_name, filename);
10254
10255         ReadUnusedBytesFromFile(file, chunk_size);
10256       }
10257       else
10258       {
10259         // call function to load this score chunk
10260         int chunk_size_expected =
10261           (chunk_info[i].loader)(file, chunk_size, &scores);
10262
10263         // the size of some chunks cannot be checked before reading other
10264         // chunks first (like "HEAD" and "BODY") that contain some header
10265         // information, so check them here
10266         if (chunk_size_expected != chunk_size)
10267         {
10268           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10269                 chunk_size, chunk_name, filename);
10270         }
10271       }
10272     }
10273   }
10274
10275   closeFile(file);
10276 }
10277
10278 #if ENABLE_HISTORIC_CHUNKS
10279 void SaveScore_OLD(int nr)
10280 {
10281   int i;
10282   char *filename = getScoreFilename(nr);
10283   FILE *file;
10284
10285   // used instead of "leveldir_current->subdir" (for network games)
10286   InitScoreDirectory(levelset.identifier);
10287
10288   if (!(file = fopen(filename, MODE_WRITE)))
10289   {
10290     Warn("cannot save score for level %d", nr);
10291
10292     return;
10293   }
10294
10295   fprintf(file, "%s\n\n", SCORE_COOKIE);
10296
10297   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10298     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10299
10300   fclose(file);
10301
10302   SetFilePermissions(filename, PERMS_PRIVATE);
10303 }
10304 #endif
10305
10306 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10307 {
10308   putFileVersion(file, scores->file_version);
10309   putFileVersion(file, scores->game_version);
10310 }
10311
10312 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10313 {
10314   int level_identifier_size = strlen(scores->level_identifier) + 1;
10315   int i;
10316
10317   putFile16BitBE(file, level_identifier_size);
10318
10319   for (i = 0; i < level_identifier_size; i++)
10320     putFile8Bit(file, scores->level_identifier[i]);
10321
10322   putFile16BitBE(file, scores->level_nr);
10323   putFile16BitBE(file, scores->num_entries);
10324 }
10325
10326 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10327 {
10328   int i, j;
10329
10330   for (i = 0; i < scores->num_entries; i++)
10331   {
10332     int name_size = strlen(scores->entry[i].name);
10333
10334     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10335       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10336   }
10337 }
10338
10339 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10340 {
10341   int i;
10342
10343   for (i = 0; i < scores->num_entries; i++)
10344     putFile16BitBE(file, scores->entry[i].score);
10345 }
10346
10347 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10348 {
10349   int i;
10350
10351   for (i = 0; i < scores->num_entries; i++)
10352     putFile32BitBE(file, scores->entry[i].score);
10353 }
10354
10355 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10356 {
10357   int i;
10358
10359   for (i = 0; i < scores->num_entries; i++)
10360     putFile32BitBE(file, scores->entry[i].time);
10361 }
10362
10363 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10364 {
10365   int i, j;
10366
10367   for (i = 0; i < scores->num_entries; i++)
10368   {
10369     int size = strlen(scores->entry[i].tape_basename);
10370
10371     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10372       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10373   }
10374 }
10375
10376 static void SaveScoreToFilename(char *filename)
10377 {
10378   FILE *file;
10379   int info_chunk_size;
10380   int name_chunk_size;
10381   int scor_chunk_size;
10382   int sc4r_chunk_size;
10383   int time_chunk_size;
10384   int tape_chunk_size;
10385   boolean has_large_score_values;
10386   int i;
10387
10388   if (!(file = fopen(filename, MODE_WRITE)))
10389   {
10390     Warn("cannot save score file '%s'", filename);
10391
10392     return;
10393   }
10394
10395   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10396   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10397   scor_chunk_size = scores.num_entries * 2;
10398   sc4r_chunk_size = scores.num_entries * 4;
10399   time_chunk_size = scores.num_entries * 4;
10400   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10401
10402   has_large_score_values = FALSE;
10403   for (i = 0; i < scores.num_entries; i++)
10404     if (scores.entry[i].score > 0xffff)
10405       has_large_score_values = TRUE;
10406
10407   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10408   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10409
10410   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10411   SaveScore_VERS(file, &scores);
10412
10413   putFileChunkBE(file, "INFO", info_chunk_size);
10414   SaveScore_INFO(file, &scores);
10415
10416   putFileChunkBE(file, "NAME", name_chunk_size);
10417   SaveScore_NAME(file, &scores);
10418
10419   if (has_large_score_values)
10420   {
10421     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10422     SaveScore_SC4R(file, &scores);
10423   }
10424   else
10425   {
10426     putFileChunkBE(file, "SCOR", scor_chunk_size);
10427     SaveScore_SCOR(file, &scores);
10428   }
10429
10430   putFileChunkBE(file, "TIME", time_chunk_size);
10431   SaveScore_TIME(file, &scores);
10432
10433   putFileChunkBE(file, "TAPE", tape_chunk_size);
10434   SaveScore_TAPE(file, &scores);
10435
10436   fclose(file);
10437
10438   SetFilePermissions(filename, PERMS_PRIVATE);
10439 }
10440
10441 void SaveScore(int nr)
10442 {
10443   char *filename = getScoreFilename(nr);
10444   int i;
10445
10446   // used instead of "leveldir_current->subdir" (for network games)
10447   InitScoreDirectory(levelset.identifier);
10448
10449   scores.file_version = FILE_VERSION_ACTUAL;
10450   scores.game_version = GAME_VERSION_ACTUAL;
10451
10452   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10453   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10454   scores.level_nr = level_nr;
10455
10456   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10457     if (scores.entry[i].score == 0 &&
10458         scores.entry[i].time == 0 &&
10459         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10460       break;
10461
10462   scores.num_entries = i;
10463
10464   if (scores.num_entries == 0)
10465     return;
10466
10467   SaveScoreToFilename(filename);
10468 }
10469
10470 static void LoadServerScoreFromCache(int nr)
10471 {
10472   struct ScoreEntry score_entry;
10473   struct
10474   {
10475     void *value;
10476     boolean is_string;
10477     int string_size;
10478   }
10479   score_mapping[] =
10480   {
10481     { &score_entry.score,               FALSE,  0                       },
10482     { &score_entry.time,                FALSE,  0                       },
10483     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10484     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10485     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10486     { &score_entry.id,                  FALSE,  0                       },
10487     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10488     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10489     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10490     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10491
10492     { NULL,                             FALSE,  0                       }
10493   };
10494   char *filename = getScoreCacheFilename(nr);
10495   SetupFileHash *score_hash = loadSetupFileHash(filename);
10496   int i, j;
10497
10498   server_scores.num_entries = 0;
10499
10500   if (score_hash == NULL)
10501     return;
10502
10503   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10504   {
10505     score_entry = server_scores.entry[i];
10506
10507     for (j = 0; score_mapping[j].value != NULL; j++)
10508     {
10509       char token[10];
10510
10511       sprintf(token, "%02d.%d", i, j);
10512
10513       char *value = getHashEntry(score_hash, token);
10514
10515       if (value == NULL)
10516         continue;
10517
10518       if (score_mapping[j].is_string)
10519       {
10520         char *score_value = (char *)score_mapping[j].value;
10521         int value_size = score_mapping[j].string_size;
10522
10523         strncpy(score_value, value, value_size);
10524         score_value[value_size] = '\0';
10525       }
10526       else
10527       {
10528         int *score_value = (int *)score_mapping[j].value;
10529
10530         *score_value = atoi(value);
10531       }
10532
10533       server_scores.num_entries = i + 1;
10534     }
10535
10536     server_scores.entry[i] = score_entry;
10537   }
10538
10539   freeSetupFileHash(score_hash);
10540 }
10541
10542 void LoadServerScore(int nr, boolean download_score)
10543 {
10544   if (!setup.use_api_server)
10545     return;
10546
10547   // always start with reliable default values
10548   setServerScoreInfoToDefaults();
10549
10550   // 1st step: load server scores from cache file (which may not exist)
10551   // (this should prevent reading it while the thread is writing to it)
10552   LoadServerScoreFromCache(nr);
10553
10554   if (download_score && runtime.use_api_server)
10555   {
10556     // 2nd step: download server scores from score server to cache file
10557     // (as thread, as it might time out if the server is not reachable)
10558     ApiGetScoreAsThread(nr);
10559   }
10560 }
10561
10562 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10563 {
10564   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10565
10566   // if score tape not uploaded, ask for uploading missing tapes later
10567   if (!setup.has_remaining_tapes)
10568     setup.ask_for_remaining_tapes = TRUE;
10569
10570   setup.provide_uploading_tapes = TRUE;
10571   setup.has_remaining_tapes = TRUE;
10572
10573   SaveSetup_ServerSetup();
10574 }
10575
10576 void SaveServerScore(int nr, boolean tape_saved)
10577 {
10578   if (!runtime.use_api_server)
10579   {
10580     PrepareScoreTapesForUpload(leveldir_current->subdir);
10581
10582     return;
10583   }
10584
10585   ApiAddScoreAsThread(nr, tape_saved, NULL);
10586 }
10587
10588 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10589                              char *score_tape_filename)
10590 {
10591   if (!runtime.use_api_server)
10592     return;
10593
10594   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10595 }
10596
10597 void LoadLocalAndServerScore(int nr, boolean download_score)
10598 {
10599   int last_added_local = scores.last_added_local;
10600   boolean force_last_added = scores.force_last_added;
10601
10602   // needed if only showing server scores
10603   setScoreInfoToDefaults();
10604
10605   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10606     LoadScore(nr);
10607
10608   // restore last added local score entry (before merging server scores)
10609   scores.last_added = scores.last_added_local = last_added_local;
10610
10611   if (setup.use_api_server &&
10612       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10613   {
10614     // load server scores from cache file and trigger update from server
10615     LoadServerScore(nr, download_score);
10616
10617     // merge local scores with scores from server
10618     MergeServerScore();
10619   }
10620
10621   if (force_last_added)
10622     scores.force_last_added = force_last_added;
10623 }
10624
10625
10626 // ============================================================================
10627 // setup file functions
10628 // ============================================================================
10629
10630 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10631
10632
10633 static struct TokenInfo global_setup_tokens[] =
10634 {
10635   {
10636     TYPE_STRING,
10637     &setup.player_name,                         "player_name"
10638   },
10639   {
10640     TYPE_SWITCH,
10641     &setup.multiple_users,                      "multiple_users"
10642   },
10643   {
10644     TYPE_SWITCH,
10645     &setup.sound,                               "sound"
10646   },
10647   {
10648     TYPE_SWITCH,
10649     &setup.sound_loops,                         "repeating_sound_loops"
10650   },
10651   {
10652     TYPE_SWITCH,
10653     &setup.sound_music,                         "background_music"
10654   },
10655   {
10656     TYPE_SWITCH,
10657     &setup.sound_simple,                        "simple_sound_effects"
10658   },
10659   {
10660     TYPE_SWITCH,
10661     &setup.toons,                               "toons"
10662   },
10663   {
10664     TYPE_SWITCH,
10665     &setup.global_animations,                   "global_animations"
10666   },
10667   {
10668     TYPE_SWITCH,
10669     &setup.scroll_delay,                        "scroll_delay"
10670   },
10671   {
10672     TYPE_SWITCH,
10673     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10674   },
10675   {
10676     TYPE_INTEGER,
10677     &setup.scroll_delay_value,                  "scroll_delay_value"
10678   },
10679   {
10680     TYPE_STRING,
10681     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10682   },
10683   {
10684     TYPE_INTEGER,
10685     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10686   },
10687   {
10688     TYPE_SWITCH,
10689     &setup.fade_screens,                        "fade_screens"
10690   },
10691   {
10692     TYPE_SWITCH,
10693     &setup.autorecord,                          "automatic_tape_recording"
10694   },
10695   {
10696     TYPE_SWITCH,
10697     &setup.autorecord_after_replay,             "autorecord_after_replay"
10698   },
10699   {
10700     TYPE_SWITCH,
10701     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10702   },
10703   {
10704     TYPE_SWITCH,
10705     &setup.show_titlescreen,                    "show_titlescreen"
10706   },
10707   {
10708     TYPE_SWITCH,
10709     &setup.quick_doors,                         "quick_doors"
10710   },
10711   {
10712     TYPE_SWITCH,
10713     &setup.team_mode,                           "team_mode"
10714   },
10715   {
10716     TYPE_SWITCH,
10717     &setup.handicap,                            "handicap"
10718   },
10719   {
10720     TYPE_SWITCH,
10721     &setup.skip_levels,                         "skip_levels"
10722   },
10723   {
10724     TYPE_SWITCH_3_STATES,
10725     &setup.allow_skipping_levels,               "allow_skipping_levels"
10726   },
10727   {
10728     TYPE_SWITCH,
10729     &setup.increment_levels,                    "increment_levels"
10730   },
10731   {
10732     TYPE_SWITCH,
10733     &setup.auto_play_next_level,                "auto_play_next_level"
10734   },
10735   {
10736     TYPE_SWITCH,
10737     &setup.count_score_after_game,              "count_score_after_game"
10738   },
10739   {
10740     TYPE_SWITCH,
10741     &setup.show_scores_after_game,              "show_scores_after_game"
10742   },
10743   {
10744     TYPE_SWITCH,
10745     &setup.time_limit,                          "time_limit"
10746   },
10747   {
10748     TYPE_SWITCH,
10749     &setup.fullscreen,                          "fullscreen"
10750   },
10751   {
10752     TYPE_INTEGER,
10753     &setup.window_scaling_percent,              "window_scaling_percent"
10754   },
10755   {
10756     TYPE_STRING,
10757     &setup.window_scaling_quality,              "window_scaling_quality"
10758   },
10759   {
10760     TYPE_STRING,
10761     &setup.screen_rendering_mode,               "screen_rendering_mode"
10762   },
10763   {
10764     TYPE_STRING,
10765     &setup.vsync_mode,                          "vsync_mode"
10766   },
10767   {
10768     TYPE_SWITCH,
10769     &setup.ask_on_escape,                       "ask_on_escape"
10770   },
10771   {
10772     TYPE_SWITCH,
10773     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10774   },
10775   {
10776     TYPE_SWITCH,
10777     &setup.ask_on_game_over,                    "ask_on_game_over"
10778   },
10779   {
10780     TYPE_SWITCH,
10781     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10782   },
10783   {
10784     TYPE_SWITCH,
10785     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10786   },
10787   {
10788     TYPE_SWITCH,
10789     &setup.quick_switch,                        "quick_player_switch"
10790   },
10791   {
10792     TYPE_SWITCH,
10793     &setup.input_on_focus,                      "input_on_focus"
10794   },
10795   {
10796     TYPE_SWITCH,
10797     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10798   },
10799   {
10800     TYPE_SWITCH,
10801     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10802   },
10803   {
10804     TYPE_SWITCH,
10805     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10806   },
10807   {
10808     TYPE_SWITCH,
10809     &setup.game_speed_extended,                 "game_speed_extended"
10810   },
10811   {
10812     TYPE_INTEGER,
10813     &setup.game_frame_delay,                    "game_frame_delay"
10814   },
10815   {
10816     TYPE_INTEGER,
10817     &setup.default_game_engine_type,            "default_game_engine_type"
10818   },
10819   {
10820     TYPE_SWITCH,
10821     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10822   },
10823   {
10824     TYPE_SWITCH,
10825     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10826   },
10827   {
10828     TYPE_SWITCH,
10829     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10830   },
10831   {
10832     TYPE_SWITCH,
10833     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10834   },
10835   {
10836     TYPE_SWITCH_3_STATES,
10837     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10838   },
10839   {
10840     TYPE_SWITCH_3_STATES,
10841     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10842   },
10843   {
10844     TYPE_SWITCH_3_STATES,
10845     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10846   },
10847   {
10848     TYPE_SWITCH_3_STATES,
10849     &setup.bd_falling_sounds,                   "bd_falling_sounds"
10850   },
10851   {
10852     TYPE_INTEGER,
10853     &setup.bd_palette_c64,                      "bd_palette_c64"
10854   },
10855   {
10856     TYPE_INTEGER,
10857     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10858   },
10859   {
10860     TYPE_INTEGER,
10861     &setup.bd_palette_atari,                    "bd_palette_atari"
10862   },
10863   {
10864     TYPE_INTEGER,
10865     &setup.bd_default_color_type,               "bd_default_color_type"
10866   },
10867   {
10868     TYPE_SWITCH,
10869     &setup.bd_random_colors,                    "bd_random_colors"
10870   },
10871   {
10872     TYPE_SWITCH,
10873     &setup.sp_show_border_elements,             "sp_show_border_elements"
10874   },
10875   {
10876     TYPE_SWITCH,
10877     &setup.small_game_graphics,                 "small_game_graphics"
10878   },
10879   {
10880     TYPE_SWITCH,
10881     &setup.show_load_save_buttons,              "show_load_save_buttons"
10882   },
10883   {
10884     TYPE_SWITCH,
10885     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10886   },
10887   {
10888     TYPE_STRING,
10889     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10890   },
10891   {
10892     TYPE_STRING,
10893     &setup.graphics_set,                        "graphics_set"
10894   },
10895   {
10896     TYPE_STRING,
10897     &setup.sounds_set,                          "sounds_set"
10898   },
10899   {
10900     TYPE_STRING,
10901     &setup.music_set,                           "music_set"
10902   },
10903   {
10904     TYPE_SWITCH_3_STATES,
10905     &setup.override_level_graphics,             "override_level_graphics"
10906   },
10907   {
10908     TYPE_SWITCH_3_STATES,
10909     &setup.override_level_sounds,               "override_level_sounds"
10910   },
10911   {
10912     TYPE_SWITCH_3_STATES,
10913     &setup.override_level_music,                "override_level_music"
10914   },
10915   {
10916     TYPE_INTEGER,
10917     &setup.volume_simple,                       "volume_simple"
10918   },
10919   {
10920     TYPE_INTEGER,
10921     &setup.volume_loops,                        "volume_loops"
10922   },
10923   {
10924     TYPE_INTEGER,
10925     &setup.volume_music,                        "volume_music"
10926   },
10927   {
10928     TYPE_SWITCH,
10929     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10930   },
10931   {
10932     TYPE_SWITCH,
10933     &setup.network_mode,                        "network_mode"
10934   },
10935   {
10936     TYPE_PLAYER,
10937     &setup.network_player_nr,                   "network_player"
10938   },
10939   {
10940     TYPE_STRING,
10941     &setup.network_server_hostname,             "network_server_hostname"
10942   },
10943   {
10944     TYPE_STRING,
10945     &setup.touch.control_type,                  "touch.control_type"
10946   },
10947   {
10948     TYPE_INTEGER,
10949     &setup.touch.move_distance,                 "touch.move_distance"
10950   },
10951   {
10952     TYPE_INTEGER,
10953     &setup.touch.drop_distance,                 "touch.drop_distance"
10954   },
10955   {
10956     TYPE_INTEGER,
10957     &setup.touch.transparency,                  "touch.transparency"
10958   },
10959   {
10960     TYPE_INTEGER,
10961     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10962   },
10963   {
10964     TYPE_INTEGER,
10965     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10966   },
10967   {
10968     TYPE_INTEGER,
10969     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10970   },
10971   {
10972     TYPE_INTEGER,
10973     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10974   },
10975   {
10976     TYPE_INTEGER,
10977     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10978   },
10979   {
10980     TYPE_INTEGER,
10981     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10982   },
10983   {
10984     TYPE_SWITCH,
10985     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10986   },
10987 };
10988
10989 static struct TokenInfo auto_setup_tokens[] =
10990 {
10991   {
10992     TYPE_INTEGER,
10993     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10994   },
10995 };
10996
10997 static struct TokenInfo server_setup_tokens[] =
10998 {
10999   {
11000     TYPE_STRING,
11001     &setup.player_uuid,                         "player_uuid"
11002   },
11003   {
11004     TYPE_INTEGER,
11005     &setup.player_version,                      "player_version"
11006   },
11007   {
11008     TYPE_SWITCH,
11009     &setup.use_api_server,          TEST_PREFIX "use_api_server"
11010   },
11011   {
11012     TYPE_STRING,
11013     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
11014   },
11015   {
11016     TYPE_STRING,
11017     &setup.api_server_password,     TEST_PREFIX "api_server_password"
11018   },
11019   {
11020     TYPE_SWITCH,
11021     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11022   },
11023   {
11024     TYPE_SWITCH,
11025     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11026   },
11027   {
11028     TYPE_SWITCH,
11029     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11030   },
11031   {
11032     TYPE_SWITCH,
11033     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11034   },
11035   {
11036     TYPE_SWITCH,
11037     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
11038   },
11039 };
11040
11041 static struct TokenInfo editor_setup_tokens[] =
11042 {
11043   {
11044     TYPE_SWITCH,
11045     &setup.editor.el_classic,                   "editor.el_classic"
11046   },
11047   {
11048     TYPE_SWITCH,
11049     &setup.editor.el_custom,                    "editor.el_custom"
11050   },
11051   {
11052     TYPE_SWITCH,
11053     &setup.editor.el_user_defined,              "editor.el_user_defined"
11054   },
11055   {
11056     TYPE_SWITCH,
11057     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11058   },
11059   {
11060     TYPE_SWITCH,
11061     &setup.editor.el_headlines,                 "editor.el_headlines"
11062   },
11063   {
11064     TYPE_SWITCH,
11065     &setup.editor.show_element_token,           "editor.show_element_token"
11066   },
11067   {
11068     TYPE_SWITCH,
11069     &setup.editor.fast_game_start,              "editor.fast_game_start"
11070   },
11071   {
11072     TYPE_SWITCH,
11073     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11074   },
11075 };
11076
11077 static struct TokenInfo editor_cascade_setup_tokens[] =
11078 {
11079   {
11080     TYPE_SWITCH,
11081     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11082   },
11083   {
11084     TYPE_SWITCH,
11085     &setup.editor_cascade.el_bdx,               "editor.cascade.el_bdx"
11086   },
11087   {
11088     TYPE_SWITCH,
11089     &setup.editor_cascade.el_bdx_effects,       "editor.cascade.el_bdx_effects"
11090   },
11091   {
11092     TYPE_SWITCH,
11093     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11094   },
11095   {
11096     TYPE_SWITCH,
11097     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11098   },
11099   {
11100     TYPE_SWITCH,
11101     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11102   },
11103   {
11104     TYPE_SWITCH,
11105     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11106   },
11107   {
11108     TYPE_SWITCH,
11109     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11110   },
11111   {
11112     TYPE_SWITCH,
11113     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11114   },
11115   {
11116     TYPE_SWITCH,
11117     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11118   },
11119   {
11120     TYPE_SWITCH,
11121     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11122   },
11123   {
11124     TYPE_SWITCH,
11125     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11126   },
11127   {
11128     TYPE_SWITCH,
11129     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11130   },
11131   {
11132     TYPE_SWITCH,
11133     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11134   },
11135   {
11136     TYPE_SWITCH,
11137     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11138   },
11139   {
11140     TYPE_SWITCH,
11141     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11142   },
11143   {
11144     TYPE_SWITCH,
11145     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11146   },
11147   {
11148     TYPE_SWITCH,
11149     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11150   },
11151   {
11152     TYPE_SWITCH,
11153     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11154   },
11155   {
11156     TYPE_SWITCH,
11157     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11158   },
11159 };
11160
11161 static struct TokenInfo shortcut_setup_tokens[] =
11162 {
11163   {
11164     TYPE_KEY_X11,
11165     &setup.shortcut.save_game,                  "shortcut.save_game"
11166   },
11167   {
11168     TYPE_KEY_X11,
11169     &setup.shortcut.load_game,                  "shortcut.load_game"
11170   },
11171   {
11172     TYPE_KEY_X11,
11173     &setup.shortcut.restart_game,               "shortcut.restart_game"
11174   },
11175   {
11176     TYPE_KEY_X11,
11177     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11178   },
11179   {
11180     TYPE_KEY_X11,
11181     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11206   },
11207   {
11208     TYPE_KEY_X11,
11209     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11210   },
11211   {
11212     TYPE_KEY_X11,
11213     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11214   },
11215   {
11216     TYPE_KEY_X11,
11217     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11218   },
11219   {
11220     TYPE_KEY_X11,
11221     &setup.shortcut.tape_record,                "shortcut.tape_record"
11222   },
11223   {
11224     TYPE_KEY_X11,
11225     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11226   },
11227   {
11228     TYPE_KEY_X11,
11229     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11230   },
11231   {
11232     TYPE_KEY_X11,
11233     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11234   },
11235   {
11236     TYPE_KEY_X11,
11237     &setup.shortcut.sound_music,                "shortcut.sound_music"
11238   },
11239   {
11240     TYPE_KEY_X11,
11241     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11242   },
11243   {
11244     TYPE_KEY_X11,
11245     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11246   },
11247   {
11248     TYPE_KEY_X11,
11249     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11250   },
11251   {
11252     TYPE_KEY_X11,
11253     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11254   },
11255   {
11256     TYPE_KEY_X11,
11257     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11258   },
11259   {
11260     TYPE_KEY_X11,
11261     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11262   },
11263 };
11264
11265 static struct SetupInputInfo setup_input;
11266 static struct TokenInfo player_setup_tokens[] =
11267 {
11268   {
11269     TYPE_BOOLEAN,
11270     &setup_input.use_joystick,                  ".use_joystick"
11271   },
11272   {
11273     TYPE_STRING,
11274     &setup_input.joy.device_name,               ".joy.device_name"
11275   },
11276   {
11277     TYPE_INTEGER,
11278     &setup_input.joy.xleft,                     ".joy.xleft"
11279   },
11280   {
11281     TYPE_INTEGER,
11282     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11283   },
11284   {
11285     TYPE_INTEGER,
11286     &setup_input.joy.xright,                    ".joy.xright"
11287   },
11288   {
11289     TYPE_INTEGER,
11290     &setup_input.joy.yupper,                    ".joy.yupper"
11291   },
11292   {
11293     TYPE_INTEGER,
11294     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11295   },
11296   {
11297     TYPE_INTEGER,
11298     &setup_input.joy.ylower,                    ".joy.ylower"
11299   },
11300   {
11301     TYPE_INTEGER,
11302     &setup_input.joy.snap,                      ".joy.snap_field"
11303   },
11304   {
11305     TYPE_INTEGER,
11306     &setup_input.joy.drop,                      ".joy.place_bomb"
11307   },
11308   {
11309     TYPE_KEY_X11,
11310     &setup_input.key.left,                      ".key.move_left"
11311   },
11312   {
11313     TYPE_KEY_X11,
11314     &setup_input.key.right,                     ".key.move_right"
11315   },
11316   {
11317     TYPE_KEY_X11,
11318     &setup_input.key.up,                        ".key.move_up"
11319   },
11320   {
11321     TYPE_KEY_X11,
11322     &setup_input.key.down,                      ".key.move_down"
11323   },
11324   {
11325     TYPE_KEY_X11,
11326     &setup_input.key.snap,                      ".key.snap_field"
11327   },
11328   {
11329     TYPE_KEY_X11,
11330     &setup_input.key.drop,                      ".key.place_bomb"
11331   },
11332 };
11333
11334 static struct TokenInfo system_setup_tokens[] =
11335 {
11336   {
11337     TYPE_STRING,
11338     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11339   },
11340   {
11341     TYPE_STRING,
11342     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11343   },
11344   {
11345     TYPE_STRING,
11346     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11347   },
11348   {
11349     TYPE_INTEGER,
11350     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11351   },
11352 };
11353
11354 static struct TokenInfo internal_setup_tokens[] =
11355 {
11356   {
11357     TYPE_STRING,
11358     &setup.internal.program_title,              "program_title"
11359   },
11360   {
11361     TYPE_STRING,
11362     &setup.internal.program_version,            "program_version"
11363   },
11364   {
11365     TYPE_STRING,
11366     &setup.internal.program_author,             "program_author"
11367   },
11368   {
11369     TYPE_STRING,
11370     &setup.internal.program_email,              "program_email"
11371   },
11372   {
11373     TYPE_STRING,
11374     &setup.internal.program_website,            "program_website"
11375   },
11376   {
11377     TYPE_STRING,
11378     &setup.internal.program_copyright,          "program_copyright"
11379   },
11380   {
11381     TYPE_STRING,
11382     &setup.internal.program_company,            "program_company"
11383   },
11384   {
11385     TYPE_STRING,
11386     &setup.internal.program_icon_file,          "program_icon_file"
11387   },
11388   {
11389     TYPE_STRING,
11390     &setup.internal.default_graphics_set,       "default_graphics_set"
11391   },
11392   {
11393     TYPE_STRING,
11394     &setup.internal.default_sounds_set,         "default_sounds_set"
11395   },
11396   {
11397     TYPE_STRING,
11398     &setup.internal.default_music_set,          "default_music_set"
11399   },
11400   {
11401     TYPE_STRING,
11402     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11403   },
11404   {
11405     TYPE_STRING,
11406     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11407   },
11408   {
11409     TYPE_STRING,
11410     &setup.internal.fallback_music_file,        "fallback_music_file"
11411   },
11412   {
11413     TYPE_STRING,
11414     &setup.internal.default_level_series,       "default_level_series"
11415   },
11416   {
11417     TYPE_INTEGER,
11418     &setup.internal.default_window_width,       "default_window_width"
11419   },
11420   {
11421     TYPE_INTEGER,
11422     &setup.internal.default_window_height,      "default_window_height"
11423   },
11424   {
11425     TYPE_BOOLEAN,
11426     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11427   },
11428   {
11429     TYPE_BOOLEAN,
11430     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11431   },
11432   {
11433     TYPE_BOOLEAN,
11434     &setup.internal.create_user_levelset,       "create_user_levelset"
11435   },
11436   {
11437     TYPE_BOOLEAN,
11438     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11439   },
11440   {
11441     TYPE_BOOLEAN,
11442     &setup.internal.menu_game,                  "menu_game"
11443   },
11444   {
11445     TYPE_BOOLEAN,
11446     &setup.internal.menu_engines,               "menu_engines"
11447   },
11448   {
11449     TYPE_BOOLEAN,
11450     &setup.internal.menu_editor,                "menu_editor"
11451   },
11452   {
11453     TYPE_BOOLEAN,
11454     &setup.internal.menu_graphics,              "menu_graphics"
11455   },
11456   {
11457     TYPE_BOOLEAN,
11458     &setup.internal.menu_sound,                 "menu_sound"
11459   },
11460   {
11461     TYPE_BOOLEAN,
11462     &setup.internal.menu_artwork,               "menu_artwork"
11463   },
11464   {
11465     TYPE_BOOLEAN,
11466     &setup.internal.menu_input,                 "menu_input"
11467   },
11468   {
11469     TYPE_BOOLEAN,
11470     &setup.internal.menu_touch,                 "menu_touch"
11471   },
11472   {
11473     TYPE_BOOLEAN,
11474     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11475   },
11476   {
11477     TYPE_BOOLEAN,
11478     &setup.internal.menu_exit,                  "menu_exit"
11479   },
11480   {
11481     TYPE_BOOLEAN,
11482     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11483   },
11484   {
11485     TYPE_BOOLEAN,
11486     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11487   },
11488   {
11489     TYPE_BOOLEAN,
11490     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11491   },
11492   {
11493     TYPE_BOOLEAN,
11494     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11495   },
11496   {
11497     TYPE_BOOLEAN,
11498     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11499   },
11500   {
11501     TYPE_BOOLEAN,
11502     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11503   },
11504   {
11505     TYPE_BOOLEAN,
11506     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11507   },
11508   {
11509     TYPE_BOOLEAN,
11510     &setup.internal.info_title,                 "info_title"
11511   },
11512   {
11513     TYPE_BOOLEAN,
11514     &setup.internal.info_elements,              "info_elements"
11515   },
11516   {
11517     TYPE_BOOLEAN,
11518     &setup.internal.info_music,                 "info_music"
11519   },
11520   {
11521     TYPE_BOOLEAN,
11522     &setup.internal.info_credits,               "info_credits"
11523   },
11524   {
11525     TYPE_BOOLEAN,
11526     &setup.internal.info_program,               "info_program"
11527   },
11528   {
11529     TYPE_BOOLEAN,
11530     &setup.internal.info_version,               "info_version"
11531   },
11532   {
11533     TYPE_BOOLEAN,
11534     &setup.internal.info_levelset,              "info_levelset"
11535   },
11536   {
11537     TYPE_BOOLEAN,
11538     &setup.internal.info_exit,                  "info_exit"
11539   },
11540 };
11541
11542 static struct TokenInfo debug_setup_tokens[] =
11543 {
11544   {
11545     TYPE_INTEGER,
11546     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11547   },
11548   {
11549     TYPE_INTEGER,
11550     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11551   },
11552   {
11553     TYPE_INTEGER,
11554     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11555   },
11556   {
11557     TYPE_INTEGER,
11558     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11559   },
11560   {
11561     TYPE_INTEGER,
11562     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11563   },
11564   {
11565     TYPE_INTEGER,
11566     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11567   },
11568   {
11569     TYPE_INTEGER,
11570     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11571   },
11572   {
11573     TYPE_INTEGER,
11574     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11575   },
11576   {
11577     TYPE_INTEGER,
11578     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11579   },
11580   {
11581     TYPE_INTEGER,
11582     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11583   },
11584   {
11585     TYPE_KEY_X11,
11586     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11587   },
11588   {
11589     TYPE_KEY_X11,
11590     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11591   },
11592   {
11593     TYPE_KEY_X11,
11594     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11595   },
11596   {
11597     TYPE_KEY_X11,
11598     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11599   },
11600   {
11601     TYPE_KEY_X11,
11602     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11603   },
11604   {
11605     TYPE_KEY_X11,
11606     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11607   },
11608   {
11609     TYPE_KEY_X11,
11610     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11611   },
11612   {
11613     TYPE_KEY_X11,
11614     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11615   },
11616   {
11617     TYPE_KEY_X11,
11618     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11619   },
11620   {
11621     TYPE_KEY_X11,
11622     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11623   },
11624   {
11625     TYPE_BOOLEAN,
11626     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11627   {
11628     TYPE_BOOLEAN,
11629     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11630   },
11631   {
11632     TYPE_BOOLEAN,
11633     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11634   },
11635   {
11636     TYPE_SWITCH_3_STATES,
11637     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11638   },
11639   {
11640     TYPE_INTEGER,
11641     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11642   },
11643 };
11644
11645 static struct TokenInfo options_setup_tokens[] =
11646 {
11647   {
11648     TYPE_BOOLEAN,
11649     &setup.options.verbose,                     "options.verbose"
11650   },
11651   {
11652     TYPE_BOOLEAN,
11653     &setup.options.debug,                       "options.debug"
11654   },
11655   {
11656     TYPE_STRING,
11657     &setup.options.debug_mode,                  "options.debug_mode"
11658   },
11659 };
11660
11661 static void setSetupInfoToDefaults(struct SetupInfo *si)
11662 {
11663   int i;
11664
11665   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11666
11667   si->multiple_users = TRUE;
11668
11669   si->sound = TRUE;
11670   si->sound_loops = TRUE;
11671   si->sound_music = TRUE;
11672   si->sound_simple = TRUE;
11673   si->toons = TRUE;
11674   si->global_animations = TRUE;
11675   si->scroll_delay = TRUE;
11676   si->forced_scroll_delay = FALSE;
11677   si->scroll_delay_value = STD_SCROLL_DELAY;
11678   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11679   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11680   si->fade_screens = TRUE;
11681   si->autorecord = TRUE;
11682   si->autorecord_after_replay = TRUE;
11683   si->auto_pause_on_start = FALSE;
11684   si->show_titlescreen = TRUE;
11685   si->quick_doors = FALSE;
11686   si->team_mode = FALSE;
11687   si->handicap = TRUE;
11688   si->skip_levels = TRUE;
11689   si->allow_skipping_levels = STATE_ASK;
11690   si->increment_levels = TRUE;
11691   si->auto_play_next_level = TRUE;
11692   si->count_score_after_game = TRUE;
11693   si->show_scores_after_game = TRUE;
11694   si->time_limit = TRUE;
11695   si->fullscreen = FALSE;
11696   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11697   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11698   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11699   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11700   si->ask_on_escape = TRUE;
11701   si->ask_on_escape_editor = TRUE;
11702   si->ask_on_game_over = TRUE;
11703   si->ask_on_quit_game = TRUE;
11704   si->ask_on_quit_program = TRUE;
11705   si->quick_switch = FALSE;
11706   si->input_on_focus = FALSE;
11707   si->prefer_aga_graphics = TRUE;
11708   si->prefer_lowpass_sounds = FALSE;
11709   si->prefer_extra_panel_items = TRUE;
11710   si->game_speed_extended = FALSE;
11711   si->game_frame_delay = GAME_FRAME_DELAY;
11712   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11713   si->bd_skip_uncovering = FALSE;
11714   si->bd_skip_hatching = FALSE;
11715   si->bd_scroll_delay = TRUE;
11716   si->bd_show_invisible_outbox = FALSE;
11717   si->bd_smooth_movements = STATE_TRUE;
11718   si->bd_pushing_graphics = STATE_TRUE;
11719   si->bd_up_down_graphics = STATE_TRUE;
11720   si->bd_falling_sounds = STATE_AUTO;
11721   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11722   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11723   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11724   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11725   si->bd_random_colors = FALSE;
11726   si->sp_show_border_elements = FALSE;
11727   si->small_game_graphics = FALSE;
11728   si->show_load_save_buttons = FALSE;
11729   si->show_undo_redo_buttons = FALSE;
11730   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11731
11732   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11733   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11734   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11735
11736   si->override_level_graphics = STATE_FALSE;
11737   si->override_level_sounds = STATE_FALSE;
11738   si->override_level_music = STATE_FALSE;
11739
11740   si->volume_simple = 100;              // percent
11741   si->volume_loops = 100;               // percent
11742   si->volume_music = 100;               // percent
11743   si->audio_sample_rate_44100 = FALSE;
11744
11745   si->network_mode = FALSE;
11746   si->network_player_nr = 0;            // first player
11747   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11748
11749   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11750   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11751   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11752   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11753   si->touch.draw_outlined = TRUE;
11754   si->touch.draw_pressed = TRUE;
11755
11756   for (i = 0; i < 2; i++)
11757   {
11758     char *default_grid_button[6][2] =
11759     {
11760       { "      ", "  ^^  " },
11761       { "      ", "  ^^  " },
11762       { "      ", "<<  >>" },
11763       { "      ", "<<  >>" },
11764       { "111222", "  vv  " },
11765       { "111222", "  vv  " }
11766     };
11767     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11768     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11769     int min_xsize = MIN(6, grid_xsize);
11770     int min_ysize = MIN(6, grid_ysize);
11771     int startx = grid_xsize - min_xsize;
11772     int starty = grid_ysize - min_ysize;
11773     int x, y;
11774
11775     // virtual buttons grid can only be set to defaults if video is initialized
11776     // (this will be repeated if virtual buttons are not loaded from setup file)
11777     if (video.initialized)
11778     {
11779       si->touch.grid_xsize[i] = grid_xsize;
11780       si->touch.grid_ysize[i] = grid_ysize;
11781     }
11782     else
11783     {
11784       si->touch.grid_xsize[i] = -1;
11785       si->touch.grid_ysize[i] = -1;
11786     }
11787
11788     for (x = 0; x < MAX_GRID_XSIZE; x++)
11789       for (y = 0; y < MAX_GRID_YSIZE; y++)
11790         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11791
11792     for (x = 0; x < min_xsize; x++)
11793       for (y = 0; y < min_ysize; y++)
11794         si->touch.grid_button[i][x][starty + y] =
11795           default_grid_button[y][0][x];
11796
11797     for (x = 0; x < min_xsize; x++)
11798       for (y = 0; y < min_ysize; y++)
11799         si->touch.grid_button[i][startx + x][starty + y] =
11800           default_grid_button[y][1][x];
11801   }
11802
11803   si->touch.grid_initialized            = video.initialized;
11804
11805   si->touch.overlay_buttons             = FALSE;
11806
11807   si->editor.el_boulderdash             = TRUE;
11808   si->editor.el_boulderdash_native      = TRUE;
11809   si->editor.el_boulderdash_effects     = TRUE;
11810   si->editor.el_emerald_mine            = TRUE;
11811   si->editor.el_emerald_mine_club       = TRUE;
11812   si->editor.el_more                    = TRUE;
11813   si->editor.el_sokoban                 = TRUE;
11814   si->editor.el_supaplex                = TRUE;
11815   si->editor.el_diamond_caves           = TRUE;
11816   si->editor.el_dx_boulderdash          = TRUE;
11817
11818   si->editor.el_mirror_magic            = TRUE;
11819   si->editor.el_deflektor               = TRUE;
11820
11821   si->editor.el_chars                   = TRUE;
11822   si->editor.el_steel_chars             = TRUE;
11823
11824   si->editor.el_classic                 = TRUE;
11825   si->editor.el_custom                  = TRUE;
11826
11827   si->editor.el_user_defined            = FALSE;
11828   si->editor.el_dynamic                 = TRUE;
11829
11830   si->editor.el_headlines               = TRUE;
11831
11832   si->editor.show_element_token         = FALSE;
11833   si->editor.fast_game_start            = FALSE;
11834
11835   si->editor.show_read_only_warning     = TRUE;
11836
11837   si->editor.use_template_for_new_levels = TRUE;
11838
11839   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11840   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11841   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11842   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11843   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11844
11845   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11846   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11847   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11848   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11849   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11850
11851   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11852   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11853   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11854   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11855   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11856   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11857
11858   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11859   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11860   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11861
11862   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11863   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11864   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11865   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11866
11867   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11868   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11869
11870   for (i = 0; i < MAX_PLAYERS; i++)
11871   {
11872     si->input[i].use_joystick = FALSE;
11873     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11874     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11875     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11876     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11877     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11878     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11879     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11880     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11881     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11882     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11883     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11884     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11885     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11886     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11887     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11888   }
11889
11890   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11891   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11892   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11893   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11894
11895   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11896   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11897   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11898   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11899   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11900   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11901   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11902
11903   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11904
11905   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11906   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11907   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11908
11909   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11910   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11911   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11912
11913   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11914   si->internal.choose_from_top_leveldir = FALSE;
11915   si->internal.show_scaling_in_title = TRUE;
11916   si->internal.create_user_levelset = TRUE;
11917   si->internal.info_screens_from_main = FALSE;
11918
11919   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11920   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11921
11922   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11923   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11924   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11925   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11926   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11927   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11928   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11929   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11930   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11931   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11932
11933   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11934   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11935   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11936   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11937   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11938   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11939   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11940   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11941   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11942   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11943
11944   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11945   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11946
11947   si->debug.show_frames_per_second = FALSE;
11948
11949   si->debug.xsn_mode = STATE_AUTO;
11950   si->debug.xsn_percent = 0;
11951
11952   si->options.verbose = FALSE;
11953   si->options.debug = FALSE;
11954   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11955
11956 #if defined(PLATFORM_ANDROID)
11957   si->fullscreen = TRUE;
11958   si->touch.overlay_buttons = TRUE;
11959 #endif
11960
11961   setHideSetupEntry(&setup.debug.xsn_mode);
11962 }
11963
11964 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11965 {
11966   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11967 }
11968
11969 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11970 {
11971   si->player_uuid = NULL;       // (will be set later)
11972   si->player_version = 1;       // (will be set later)
11973
11974   si->use_api_server = TRUE;
11975   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11976   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11977   si->ask_for_uploading_tapes = TRUE;
11978   si->ask_for_remaining_tapes = FALSE;
11979   si->provide_uploading_tapes = TRUE;
11980   si->ask_for_using_api_server = TRUE;
11981   si->has_remaining_tapes = FALSE;
11982 }
11983
11984 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11985 {
11986   si->editor_cascade.el_bd              = TRUE;
11987   si->editor_cascade.el_bdx             = TRUE;
11988   si->editor_cascade.el_bdx_effects     = FALSE;
11989   si->editor_cascade.el_em              = TRUE;
11990   si->editor_cascade.el_emc             = TRUE;
11991   si->editor_cascade.el_rnd             = TRUE;
11992   si->editor_cascade.el_sb              = TRUE;
11993   si->editor_cascade.el_sp              = TRUE;
11994   si->editor_cascade.el_dc              = TRUE;
11995   si->editor_cascade.el_dx              = TRUE;
11996
11997   si->editor_cascade.el_mm              = TRUE;
11998   si->editor_cascade.el_df              = TRUE;
11999
12000   si->editor_cascade.el_chars           = FALSE;
12001   si->editor_cascade.el_steel_chars     = FALSE;
12002   si->editor_cascade.el_ce              = FALSE;
12003   si->editor_cascade.el_ge              = FALSE;
12004   si->editor_cascade.el_es              = FALSE;
12005   si->editor_cascade.el_ref             = FALSE;
12006   si->editor_cascade.el_user            = FALSE;
12007   si->editor_cascade.el_dynamic         = FALSE;
12008 }
12009
12010 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
12011
12012 static char *getHideSetupToken(void *setup_value)
12013 {
12014   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12015
12016   if (setup_value != NULL)
12017     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12018
12019   return hide_setup_token;
12020 }
12021
12022 void setHideSetupEntry(void *setup_value)
12023 {
12024   char *hide_setup_token = getHideSetupToken(setup_value);
12025
12026   if (hide_setup_hash == NULL)
12027     hide_setup_hash = newSetupFileHash();
12028
12029   if (setup_value != NULL)
12030     setHashEntry(hide_setup_hash, hide_setup_token, "");
12031 }
12032
12033 void removeHideSetupEntry(void *setup_value)
12034 {
12035   char *hide_setup_token = getHideSetupToken(setup_value);
12036
12037   if (setup_value != NULL)
12038     removeHashEntry(hide_setup_hash, hide_setup_token);
12039 }
12040
12041 boolean hideSetupEntry(void *setup_value)
12042 {
12043   char *hide_setup_token = getHideSetupToken(setup_value);
12044
12045   return (setup_value != NULL &&
12046           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12047 }
12048
12049 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12050                                       struct TokenInfo *token_info,
12051                                       int token_nr, char *token_text)
12052 {
12053   char *token_hide_text = getStringCat2(token_text, ".hide");
12054   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12055
12056   // set the value of this setup option in the setup option structure
12057   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12058
12059   // check if this setup option should be hidden in the setup menu
12060   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12061     setHideSetupEntry(token_info[token_nr].value);
12062
12063   free(token_hide_text);
12064 }
12065
12066 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12067                                       struct TokenInfo *token_info,
12068                                       int token_nr)
12069 {
12070   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12071                             token_info[token_nr].text);
12072 }
12073
12074 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12075 {
12076   int i, pnr;
12077
12078   if (!setup_file_hash)
12079     return;
12080
12081   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12082     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12083
12084   setup.touch.grid_initialized = TRUE;
12085   for (i = 0; i < 2; i++)
12086   {
12087     int grid_xsize = setup.touch.grid_xsize[i];
12088     int grid_ysize = setup.touch.grid_ysize[i];
12089     int x, y;
12090
12091     // if virtual buttons are not loaded from setup file, repeat initializing
12092     // virtual buttons grid with default values later when video is initialized
12093     if (grid_xsize == -1 ||
12094         grid_ysize == -1)
12095     {
12096       setup.touch.grid_initialized = FALSE;
12097
12098       continue;
12099     }
12100
12101     for (y = 0; y < grid_ysize; y++)
12102     {
12103       char token_string[MAX_LINE_LEN];
12104
12105       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12106
12107       char *value_string = getHashEntry(setup_file_hash, token_string);
12108
12109       if (value_string == NULL)
12110         continue;
12111
12112       for (x = 0; x < grid_xsize; x++)
12113       {
12114         char c = value_string[x];
12115
12116         setup.touch.grid_button[i][x][y] =
12117           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12118       }
12119     }
12120   }
12121
12122   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12123     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12124
12125   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12126     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12127
12128   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12129   {
12130     char prefix[30];
12131
12132     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12133
12134     setup_input = setup.input[pnr];
12135     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12136     {
12137       char full_token[100];
12138
12139       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12140       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12141                                 full_token);
12142     }
12143     setup.input[pnr] = setup_input;
12144   }
12145
12146   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12147     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12148
12149   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12150     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12151
12152   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12153     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12154
12155   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12156     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12157
12158   setHideRelatedSetupEntries();
12159 }
12160
12161 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12162 {
12163   int i;
12164
12165   if (!setup_file_hash)
12166     return;
12167
12168   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12169     setSetupInfo(auto_setup_tokens, i,
12170                  getHashEntry(setup_file_hash,
12171                               auto_setup_tokens[i].text));
12172 }
12173
12174 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12175 {
12176   int i;
12177
12178   if (!setup_file_hash)
12179     return;
12180
12181   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12182     setSetupInfo(server_setup_tokens, i,
12183                  getHashEntry(setup_file_hash,
12184                               server_setup_tokens[i].text));
12185 }
12186
12187 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12188 {
12189   int i;
12190
12191   if (!setup_file_hash)
12192     return;
12193
12194   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12195     setSetupInfo(editor_cascade_setup_tokens, i,
12196                  getHashEntry(setup_file_hash,
12197                               editor_cascade_setup_tokens[i].text));
12198 }
12199
12200 void LoadUserNames(void)
12201 {
12202   int last_user_nr = user.nr;
12203   int i;
12204
12205   if (global.user_names != NULL)
12206   {
12207     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12208       checked_free(global.user_names[i]);
12209
12210     checked_free(global.user_names);
12211   }
12212
12213   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12214
12215   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12216   {
12217     user.nr = i;
12218
12219     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12220
12221     if (setup_file_hash)
12222     {
12223       char *player_name = getHashEntry(setup_file_hash, "player_name");
12224
12225       global.user_names[i] = getFixedUserName(player_name);
12226
12227       freeSetupFileHash(setup_file_hash);
12228     }
12229
12230     if (global.user_names[i] == NULL)
12231       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12232   }
12233
12234   user.nr = last_user_nr;
12235 }
12236
12237 void LoadSetupFromFilename(char *filename)
12238 {
12239   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12240
12241   if (setup_file_hash)
12242   {
12243     decodeSetupFileHash_Default(setup_file_hash);
12244
12245     freeSetupFileHash(setup_file_hash);
12246   }
12247   else
12248   {
12249     Debug("setup", "using default setup values");
12250   }
12251 }
12252
12253 static void LoadSetup_SpecialPostProcessing(void)
12254 {
12255   char *player_name_new;
12256
12257   // needed to work around problems with fixed length strings
12258   player_name_new = getFixedUserName(setup.player_name);
12259   free(setup.player_name);
12260   setup.player_name = player_name_new;
12261
12262   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12263   if (setup.scroll_delay == FALSE)
12264   {
12265     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12266     setup.scroll_delay = TRUE;                  // now always "on"
12267   }
12268
12269   // make sure that scroll delay value stays inside valid range
12270   setup.scroll_delay_value =
12271     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12272 }
12273
12274 void LoadSetup_Default(void)
12275 {
12276   char *filename;
12277
12278   // always start with reliable default values
12279   setSetupInfoToDefaults(&setup);
12280
12281   // try to load setup values from default setup file
12282   filename = getDefaultSetupFilename();
12283
12284   if (fileExists(filename))
12285     LoadSetupFromFilename(filename);
12286
12287   // try to load setup values from platform setup file
12288   filename = getPlatformSetupFilename();
12289
12290   if (fileExists(filename))
12291     LoadSetupFromFilename(filename);
12292
12293   // try to load setup values from user setup file
12294   filename = getSetupFilename();
12295
12296   LoadSetupFromFilename(filename);
12297
12298   LoadSetup_SpecialPostProcessing();
12299 }
12300
12301 void LoadSetup_AutoSetup(void)
12302 {
12303   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12304   SetupFileHash *setup_file_hash = NULL;
12305
12306   // always start with reliable default values
12307   setSetupInfoToDefaults_AutoSetup(&setup);
12308
12309   setup_file_hash = loadSetupFileHash(filename);
12310
12311   if (setup_file_hash)
12312   {
12313     decodeSetupFileHash_AutoSetup(setup_file_hash);
12314
12315     freeSetupFileHash(setup_file_hash);
12316   }
12317
12318   free(filename);
12319 }
12320
12321 void LoadSetup_ServerSetup(void)
12322 {
12323   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12324   SetupFileHash *setup_file_hash = NULL;
12325
12326   // always start with reliable default values
12327   setSetupInfoToDefaults_ServerSetup(&setup);
12328
12329   setup_file_hash = loadSetupFileHash(filename);
12330
12331   if (setup_file_hash)
12332   {
12333     decodeSetupFileHash_ServerSetup(setup_file_hash);
12334
12335     freeSetupFileHash(setup_file_hash);
12336   }
12337
12338   free(filename);
12339
12340   if (setup.player_uuid == NULL)
12341   {
12342     // player UUID does not yet exist in setup file
12343     setup.player_uuid = getStringCopy(getUUID());
12344     setup.player_version = 2;
12345
12346     SaveSetup_ServerSetup();
12347   }
12348 }
12349
12350 void LoadSetup_EditorCascade(void)
12351 {
12352   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12353   SetupFileHash *setup_file_hash = NULL;
12354
12355   // always start with reliable default values
12356   setSetupInfoToDefaults_EditorCascade(&setup);
12357
12358   setup_file_hash = loadSetupFileHash(filename);
12359
12360   if (setup_file_hash)
12361   {
12362     decodeSetupFileHash_EditorCascade(setup_file_hash);
12363
12364     freeSetupFileHash(setup_file_hash);
12365   }
12366
12367   free(filename);
12368 }
12369
12370 void LoadSetup(void)
12371 {
12372   LoadSetup_Default();
12373   LoadSetup_AutoSetup();
12374   LoadSetup_ServerSetup();
12375   LoadSetup_EditorCascade();
12376 }
12377
12378 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12379                                            char *mapping_line)
12380 {
12381   char mapping_guid[MAX_LINE_LEN];
12382   char *mapping_start, *mapping_end;
12383
12384   // get GUID from game controller mapping line: copy complete line
12385   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12386   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12387
12388   // get GUID from game controller mapping line: cut after GUID part
12389   mapping_start = strchr(mapping_guid, ',');
12390   if (mapping_start != NULL)
12391     *mapping_start = '\0';
12392
12393   // cut newline from game controller mapping line
12394   mapping_end = strchr(mapping_line, '\n');
12395   if (mapping_end != NULL)
12396     *mapping_end = '\0';
12397
12398   // add mapping entry to game controller mappings hash
12399   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12400 }
12401
12402 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12403                                                  char *filename)
12404 {
12405   FILE *file;
12406
12407   if (!(file = fopen(filename, MODE_READ)))
12408   {
12409     Warn("cannot read game controller mappings file '%s'", filename);
12410
12411     return;
12412   }
12413
12414   while (!feof(file))
12415   {
12416     char line[MAX_LINE_LEN];
12417
12418     if (!fgets(line, MAX_LINE_LEN, file))
12419       break;
12420
12421     addGameControllerMappingToHash(mappings_hash, line);
12422   }
12423
12424   fclose(file);
12425 }
12426
12427 void SaveSetup_Default(void)
12428 {
12429   char *filename = getSetupFilename();
12430   FILE *file;
12431   int i, pnr;
12432
12433   InitUserDataDirectory();
12434
12435   if (!(file = fopen(filename, MODE_WRITE)))
12436   {
12437     Warn("cannot write setup file '%s'", filename);
12438
12439     return;
12440   }
12441
12442   fprintFileHeader(file, SETUP_FILENAME);
12443
12444   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12445   {
12446     // just to make things nicer :)
12447     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12448         global_setup_tokens[i].value == &setup.sound                    ||
12449         global_setup_tokens[i].value == &setup.graphics_set             ||
12450         global_setup_tokens[i].value == &setup.volume_simple            ||
12451         global_setup_tokens[i].value == &setup.network_mode             ||
12452         global_setup_tokens[i].value == &setup.touch.control_type       ||
12453         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12454         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12455       fprintf(file, "\n");
12456
12457     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12458   }
12459
12460   for (i = 0; i < 2; i++)
12461   {
12462     int grid_xsize = setup.touch.grid_xsize[i];
12463     int grid_ysize = setup.touch.grid_ysize[i];
12464     int x, y;
12465
12466     fprintf(file, "\n");
12467
12468     for (y = 0; y < grid_ysize; y++)
12469     {
12470       char token_string[MAX_LINE_LEN];
12471       char value_string[MAX_LINE_LEN];
12472
12473       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12474
12475       for (x = 0; x < grid_xsize; x++)
12476       {
12477         char c = setup.touch.grid_button[i][x][y];
12478
12479         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12480       }
12481
12482       value_string[grid_xsize] = '\0';
12483
12484       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12485     }
12486   }
12487
12488   fprintf(file, "\n");
12489   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12490     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12491
12492   fprintf(file, "\n");
12493   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12494     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12495
12496   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12497   {
12498     char prefix[30];
12499
12500     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12501     fprintf(file, "\n");
12502
12503     setup_input = setup.input[pnr];
12504     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12505       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12506   }
12507
12508   fprintf(file, "\n");
12509   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12510     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12511
12512   // (internal setup values not saved to user setup file)
12513
12514   fprintf(file, "\n");
12515   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12516     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12517         setup.debug.xsn_mode != STATE_AUTO)
12518       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12519
12520   fprintf(file, "\n");
12521   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12522     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12523
12524   fclose(file);
12525
12526   SetFilePermissions(filename, PERMS_PRIVATE);
12527 }
12528
12529 void SaveSetup_AutoSetup(void)
12530 {
12531   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12532   FILE *file;
12533   int i;
12534
12535   InitUserDataDirectory();
12536
12537   if (!(file = fopen(filename, MODE_WRITE)))
12538   {
12539     Warn("cannot write auto setup file '%s'", filename);
12540
12541     free(filename);
12542
12543     return;
12544   }
12545
12546   fprintFileHeader(file, AUTOSETUP_FILENAME);
12547
12548   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12549     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12550
12551   fclose(file);
12552
12553   SetFilePermissions(filename, PERMS_PRIVATE);
12554
12555   free(filename);
12556 }
12557
12558 void SaveSetup_ServerSetup(void)
12559 {
12560   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12561   FILE *file;
12562   int i;
12563
12564   InitUserDataDirectory();
12565
12566   if (!(file = fopen(filename, MODE_WRITE)))
12567   {
12568     Warn("cannot write server setup file '%s'", filename);
12569
12570     free(filename);
12571
12572     return;
12573   }
12574
12575   fprintFileHeader(file, SERVERSETUP_FILENAME);
12576
12577   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12578   {
12579     // just to make things nicer :)
12580     if (server_setup_tokens[i].value == &setup.use_api_server)
12581       fprintf(file, "\n");
12582
12583     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12584   }
12585
12586   fclose(file);
12587
12588   SetFilePermissions(filename, PERMS_PRIVATE);
12589
12590   free(filename);
12591 }
12592
12593 void SaveSetup_EditorCascade(void)
12594 {
12595   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12596   FILE *file;
12597   int i;
12598
12599   InitUserDataDirectory();
12600
12601   if (!(file = fopen(filename, MODE_WRITE)))
12602   {
12603     Warn("cannot write editor cascade state file '%s'", filename);
12604
12605     free(filename);
12606
12607     return;
12608   }
12609
12610   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12611
12612   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12613     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12614
12615   fclose(file);
12616
12617   SetFilePermissions(filename, PERMS_PRIVATE);
12618
12619   free(filename);
12620 }
12621
12622 void SaveSetup(void)
12623 {
12624   SaveSetup_Default();
12625   SaveSetup_AutoSetup();
12626   SaveSetup_ServerSetup();
12627   SaveSetup_EditorCascade();
12628 }
12629
12630 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12631                                                   char *filename)
12632 {
12633   FILE *file;
12634
12635   if (!(file = fopen(filename, MODE_WRITE)))
12636   {
12637     Warn("cannot write game controller mappings file '%s'", filename);
12638
12639     return;
12640   }
12641
12642   BEGIN_HASH_ITERATION(mappings_hash, itr)
12643   {
12644     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12645   }
12646   END_HASH_ITERATION(mappings_hash, itr)
12647
12648   fclose(file);
12649 }
12650
12651 void SaveSetup_AddGameControllerMapping(char *mapping)
12652 {
12653   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12654   SetupFileHash *mappings_hash = newSetupFileHash();
12655
12656   InitUserDataDirectory();
12657
12658   // load existing personal game controller mappings
12659   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12660
12661   // add new mapping to personal game controller mappings
12662   addGameControllerMappingToHash(mappings_hash, mapping);
12663
12664   // save updated personal game controller mappings
12665   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12666
12667   freeSetupFileHash(mappings_hash);
12668   free(filename);
12669 }
12670
12671 void LoadCustomElementDescriptions(void)
12672 {
12673   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12674   SetupFileHash *setup_file_hash;
12675   int i;
12676
12677   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12678   {
12679     if (element_info[i].custom_description != NULL)
12680     {
12681       free(element_info[i].custom_description);
12682       element_info[i].custom_description = NULL;
12683     }
12684   }
12685
12686   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12687     return;
12688
12689   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12690   {
12691     char *token = getStringCat2(element_info[i].token_name, ".name");
12692     char *value = getHashEntry(setup_file_hash, token);
12693
12694     if (value != NULL)
12695       element_info[i].custom_description = getStringCopy(value);
12696
12697     free(token);
12698   }
12699
12700   freeSetupFileHash(setup_file_hash);
12701 }
12702
12703 static int getElementFromToken(char *token)
12704 {
12705   char *value = getHashEntry(element_token_hash, token);
12706
12707   if (value != NULL)
12708     return atoi(value);
12709
12710   Warn("unknown element token '%s'", token);
12711
12712   return EL_UNDEFINED;
12713 }
12714
12715 void FreeGlobalAnimEventInfo(void)
12716 {
12717   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12718
12719   if (gaei->event_list == NULL)
12720     return;
12721
12722   int i;
12723
12724   for (i = 0; i < gaei->num_event_lists; i++)
12725   {
12726     checked_free(gaei->event_list[i]->event_value);
12727     checked_free(gaei->event_list[i]);
12728   }
12729
12730   checked_free(gaei->event_list);
12731
12732   gaei->event_list = NULL;
12733   gaei->num_event_lists = 0;
12734 }
12735
12736 static int AddGlobalAnimEventList(void)
12737 {
12738   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12739   int list_pos = gaei->num_event_lists++;
12740
12741   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12742                                      sizeof(struct GlobalAnimEventListInfo *));
12743
12744   gaei->event_list[list_pos] =
12745     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12746
12747   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12748
12749   gaeli->event_value = NULL;
12750   gaeli->num_event_values = 0;
12751
12752   return list_pos;
12753 }
12754
12755 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12756 {
12757   // do not add empty global animation events
12758   if (event_value == ANIM_EVENT_NONE)
12759     return list_pos;
12760
12761   // if list position is undefined, create new list
12762   if (list_pos == ANIM_EVENT_UNDEFINED)
12763     list_pos = AddGlobalAnimEventList();
12764
12765   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12766   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12767   int value_pos = gaeli->num_event_values++;
12768
12769   gaeli->event_value = checked_realloc(gaeli->event_value,
12770                                        gaeli->num_event_values * sizeof(int *));
12771
12772   gaeli->event_value[value_pos] = event_value;
12773
12774   return list_pos;
12775 }
12776
12777 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12778 {
12779   if (list_pos == ANIM_EVENT_UNDEFINED)
12780     return ANIM_EVENT_NONE;
12781
12782   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12783   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12784
12785   return gaeli->event_value[value_pos];
12786 }
12787
12788 int GetGlobalAnimEventValueCount(int list_pos)
12789 {
12790   if (list_pos == ANIM_EVENT_UNDEFINED)
12791     return 0;
12792
12793   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12794   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12795
12796   return gaeli->num_event_values;
12797 }
12798
12799 // This function checks if a string <s> of the format "string1, string2, ..."
12800 // exactly contains a string <s_contained>.
12801
12802 static boolean string_has_parameter(char *s, char *s_contained)
12803 {
12804   char *substring;
12805
12806   if (s == NULL || s_contained == NULL)
12807     return FALSE;
12808
12809   if (strlen(s_contained) > strlen(s))
12810     return FALSE;
12811
12812   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12813   {
12814     char next_char = s[strlen(s_contained)];
12815
12816     // check if next character is delimiter or whitespace
12817     if (next_char == ',' || next_char == '\0' ||
12818         next_char == ' ' || next_char == '\t')
12819       return TRUE;
12820   }
12821
12822   // check if string contains another parameter string after a comma
12823   substring = strchr(s, ',');
12824   if (substring == NULL)        // string does not contain a comma
12825     return FALSE;
12826
12827   // advance string pointer to next character after the comma
12828   substring++;
12829
12830   // skip potential whitespaces after the comma
12831   while (*substring == ' ' || *substring == '\t')
12832     substring++;
12833
12834   return string_has_parameter(substring, s_contained);
12835 }
12836
12837 static int get_anim_parameter_value_ce(char *s)
12838 {
12839   char *s_ptr = s;
12840   char *pattern_1 = "ce_change:custom_";
12841   char *pattern_2 = ".page_";
12842   int pattern_1_len = strlen(pattern_1);
12843   char *matching_char = strstr(s_ptr, pattern_1);
12844   int result = ANIM_EVENT_NONE;
12845
12846   if (matching_char == NULL)
12847     return ANIM_EVENT_NONE;
12848
12849   result = ANIM_EVENT_CE_CHANGE;
12850
12851   s_ptr = matching_char + pattern_1_len;
12852
12853   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12854   if (*s_ptr >= '0' && *s_ptr <= '9')
12855   {
12856     int gic_ce_nr = (*s_ptr++ - '0');
12857
12858     if (*s_ptr >= '0' && *s_ptr <= '9')
12859     {
12860       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12861
12862       if (*s_ptr >= '0' && *s_ptr <= '9')
12863         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12864     }
12865
12866     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12867       return ANIM_EVENT_NONE;
12868
12869     // custom element stored as 0 to 255
12870     gic_ce_nr--;
12871
12872     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12873   }
12874   else
12875   {
12876     // invalid custom element number specified
12877
12878     return ANIM_EVENT_NONE;
12879   }
12880
12881   // check for change page number ("page_X" or "page_XX") (optional)
12882   if (strPrefix(s_ptr, pattern_2))
12883   {
12884     s_ptr += strlen(pattern_2);
12885
12886     if (*s_ptr >= '0' && *s_ptr <= '9')
12887     {
12888       int gic_page_nr = (*s_ptr++ - '0');
12889
12890       if (*s_ptr >= '0' && *s_ptr <= '9')
12891         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12892
12893       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12894         return ANIM_EVENT_NONE;
12895
12896       // change page stored as 1 to 32 (0 means "all change pages")
12897
12898       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12899     }
12900     else
12901     {
12902       // invalid animation part number specified
12903
12904       return ANIM_EVENT_NONE;
12905     }
12906   }
12907
12908   // discard result if next character is neither delimiter nor whitespace
12909   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12910         *s_ptr == ' ' || *s_ptr == '\t'))
12911     return ANIM_EVENT_NONE;
12912
12913   return result;
12914 }
12915
12916 static int get_anim_parameter_value(char *s)
12917 {
12918   int event_value[] =
12919   {
12920     ANIM_EVENT_CLICK,
12921     ANIM_EVENT_INIT,
12922     ANIM_EVENT_START,
12923     ANIM_EVENT_END,
12924     ANIM_EVENT_POST
12925   };
12926   char *pattern_1[] =
12927   {
12928     "click:anim_",
12929     "init:anim_",
12930     "start:anim_",
12931     "end:anim_",
12932     "post:anim_"
12933   };
12934   char *pattern_2 = ".part_";
12935   char *matching_char = NULL;
12936   char *s_ptr = s;
12937   int pattern_1_len = 0;
12938   int result = ANIM_EVENT_NONE;
12939   int i;
12940
12941   result = get_anim_parameter_value_ce(s);
12942
12943   if (result != ANIM_EVENT_NONE)
12944     return result;
12945
12946   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12947   {
12948     matching_char = strstr(s_ptr, pattern_1[i]);
12949     pattern_1_len = strlen(pattern_1[i]);
12950     result = event_value[i];
12951
12952     if (matching_char != NULL)
12953       break;
12954   }
12955
12956   if (matching_char == NULL)
12957     return ANIM_EVENT_NONE;
12958
12959   s_ptr = matching_char + pattern_1_len;
12960
12961   // check for main animation number ("anim_X" or "anim_XX")
12962   if (*s_ptr >= '0' && *s_ptr <= '9')
12963   {
12964     int gic_anim_nr = (*s_ptr++ - '0');
12965
12966     if (*s_ptr >= '0' && *s_ptr <= '9')
12967       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12968
12969     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12970       return ANIM_EVENT_NONE;
12971
12972     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12973   }
12974   else
12975   {
12976     // invalid main animation number specified
12977
12978     return ANIM_EVENT_NONE;
12979   }
12980
12981   // check for animation part number ("part_X" or "part_XX") (optional)
12982   if (strPrefix(s_ptr, pattern_2))
12983   {
12984     s_ptr += strlen(pattern_2);
12985
12986     if (*s_ptr >= '0' && *s_ptr <= '9')
12987     {
12988       int gic_part_nr = (*s_ptr++ - '0');
12989
12990       if (*s_ptr >= '0' && *s_ptr <= '9')
12991         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12992
12993       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12994         return ANIM_EVENT_NONE;
12995
12996       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12997     }
12998     else
12999     {
13000       // invalid animation part number specified
13001
13002       return ANIM_EVENT_NONE;
13003     }
13004   }
13005
13006   // discard result if next character is neither delimiter nor whitespace
13007   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13008         *s_ptr == ' ' || *s_ptr == '\t'))
13009     return ANIM_EVENT_NONE;
13010
13011   return result;
13012 }
13013
13014 static int get_anim_parameter_values(char *s)
13015 {
13016   int list_pos = ANIM_EVENT_UNDEFINED;
13017   int event_value = ANIM_EVENT_DEFAULT;
13018
13019   if (string_has_parameter(s, "any"))
13020     event_value |= ANIM_EVENT_ANY;
13021
13022   if (string_has_parameter(s, "click:self") ||
13023       string_has_parameter(s, "click") ||
13024       string_has_parameter(s, "self"))
13025     event_value |= ANIM_EVENT_SELF;
13026
13027   if (string_has_parameter(s, "unclick:any"))
13028     event_value |= ANIM_EVENT_UNCLICK_ANY;
13029
13030   // if animation event found, add it to global animation event list
13031   if (event_value != ANIM_EVENT_NONE)
13032     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13033
13034   while (s != NULL)
13035   {
13036     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13037     event_value = get_anim_parameter_value(s);
13038
13039     // if animation event found, add it to global animation event list
13040     if (event_value != ANIM_EVENT_NONE)
13041       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13042
13043     // continue with next part of the string, starting with next comma
13044     s = strchr(s + 1, ',');
13045   }
13046
13047   return list_pos;
13048 }
13049
13050 static int get_anim_action_parameter_value(char *token)
13051 {
13052   // check most common default case first to massively speed things up
13053   if (strEqual(token, ARG_UNDEFINED))
13054     return ANIM_EVENT_ACTION_NONE;
13055
13056   int result = getImageIDFromToken(token);
13057
13058   if (result == -1)
13059   {
13060     char *gfx_token = getStringCat2("gfx.", token);
13061
13062     result = getImageIDFromToken(gfx_token);
13063
13064     checked_free(gfx_token);
13065   }
13066
13067   if (result == -1)
13068   {
13069     Key key = getKeyFromX11KeyName(token);
13070
13071     if (key != KSYM_UNDEFINED)
13072       result = -(int)key;
13073   }
13074
13075   if (result == -1)
13076   {
13077     if (isURL(token))
13078     {
13079       result = get_hash_from_string(token);     // unsigned int => int
13080       result = ABS(result);                     // may be negative now
13081       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13082
13083       setHashEntry(anim_url_hash, int2str(result, 0), token);
13084     }
13085   }
13086
13087   if (result == -1)
13088     result = ANIM_EVENT_ACTION_NONE;
13089
13090   return result;
13091 }
13092
13093 int get_parameter_value(char *value_raw, char *suffix, int type)
13094 {
13095   char *value = getStringToLower(value_raw);
13096   int result = 0;       // probably a save default value
13097
13098   if (strEqual(suffix, ".direction"))
13099   {
13100     result = (strEqual(value, "left")  ? MV_LEFT :
13101               strEqual(value, "right") ? MV_RIGHT :
13102               strEqual(value, "up")    ? MV_UP :
13103               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13104   }
13105   else if (strEqual(suffix, ".position"))
13106   {
13107     result = (strEqual(value, "left")   ? POS_LEFT :
13108               strEqual(value, "right")  ? POS_RIGHT :
13109               strEqual(value, "top")    ? POS_TOP :
13110               strEqual(value, "upper")  ? POS_UPPER :
13111               strEqual(value, "middle") ? POS_MIDDLE :
13112               strEqual(value, "lower")  ? POS_LOWER :
13113               strEqual(value, "bottom") ? POS_BOTTOM :
13114               strEqual(value, "any")    ? POS_ANY :
13115               strEqual(value, "ce")     ? POS_CE :
13116               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13117               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13118   }
13119   else if (strEqual(suffix, ".align"))
13120   {
13121     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13122               strEqual(value, "right")  ? ALIGN_RIGHT :
13123               strEqual(value, "center") ? ALIGN_CENTER :
13124               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13125   }
13126   else if (strEqual(suffix, ".valign"))
13127   {
13128     result = (strEqual(value, "top")    ? VALIGN_TOP :
13129               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13130               strEqual(value, "middle") ? VALIGN_MIDDLE :
13131               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13132   }
13133   else if (strEqual(suffix, ".anim_mode"))
13134   {
13135     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13136               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13137               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13138               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13139               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13140               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13141               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13142               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13143               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13144               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13145               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13146               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13147               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13148               string_has_parameter(value, "all")        ? ANIM_ALL :
13149               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13150               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13151               ANIM_DEFAULT);
13152
13153     if (string_has_parameter(value, "once"))
13154       result |= ANIM_ONCE;
13155
13156     if (string_has_parameter(value, "reverse"))
13157       result |= ANIM_REVERSE;
13158
13159     if (string_has_parameter(value, "opaque_player"))
13160       result |= ANIM_OPAQUE_PLAYER;
13161
13162     if (string_has_parameter(value, "static_panel"))
13163       result |= ANIM_STATIC_PANEL;
13164   }
13165   else if (strEqual(suffix, ".init_event") ||
13166            strEqual(suffix, ".anim_event"))
13167   {
13168     result = get_anim_parameter_values(value);
13169   }
13170   else if (strEqual(suffix, ".init_delay_action") ||
13171            strEqual(suffix, ".anim_delay_action") ||
13172            strEqual(suffix, ".post_delay_action") ||
13173            strEqual(suffix, ".init_event_action") ||
13174            strEqual(suffix, ".anim_event_action"))
13175   {
13176     result = get_anim_action_parameter_value(value_raw);
13177   }
13178   else if (strEqual(suffix, ".class"))
13179   {
13180     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13181               get_hash_from_string(value));
13182   }
13183   else if (strEqual(suffix, ".style"))
13184   {
13185     result = STYLE_DEFAULT;
13186
13187     if (string_has_parameter(value, "accurate_borders"))
13188       result |= STYLE_ACCURATE_BORDERS;
13189
13190     if (string_has_parameter(value, "inner_corners"))
13191       result |= STYLE_INNER_CORNERS;
13192
13193     if (string_has_parameter(value, "reverse"))
13194       result |= STYLE_REVERSE;
13195
13196     if (string_has_parameter(value, "leftmost_position"))
13197       result |= STYLE_LEFTMOST_POSITION;
13198
13199     if (string_has_parameter(value, "block_clicks"))
13200       result |= STYLE_BLOCK;
13201
13202     if (string_has_parameter(value, "passthrough_clicks"))
13203       result |= STYLE_PASSTHROUGH;
13204
13205     if (string_has_parameter(value, "multiple_actions"))
13206       result |= STYLE_MULTIPLE_ACTIONS;
13207
13208     if (string_has_parameter(value, "consume_ce_event"))
13209       result |= STYLE_CONSUME_CE_EVENT;
13210   }
13211   else if (strEqual(suffix, ".fade_mode"))
13212   {
13213     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13214               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13215               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13216               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13217               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13218               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13219               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13220               FADE_MODE_DEFAULT);
13221   }
13222   else if (strEqual(suffix, ".auto_delay_unit"))
13223   {
13224     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13225               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13226               AUTO_DELAY_UNIT_DEFAULT);
13227   }
13228   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13229   {
13230     result = gfx.get_font_from_token_function(value);
13231   }
13232   else          // generic parameter of type integer or boolean
13233   {
13234     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13235               type == TYPE_INTEGER ? get_integer_from_string(value) :
13236               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13237               ARG_UNDEFINED_VALUE);
13238   }
13239
13240   free(value);
13241
13242   return result;
13243 }
13244
13245 static int get_token_parameter_value(char *token, char *value_raw)
13246 {
13247   char *suffix;
13248
13249   if (token == NULL || value_raw == NULL)
13250     return ARG_UNDEFINED_VALUE;
13251
13252   suffix = strrchr(token, '.');
13253   if (suffix == NULL)
13254     suffix = token;
13255
13256   if (strEqual(suffix, ".element"))
13257     return getElementFromToken(value_raw);
13258
13259   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13260   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13261 }
13262
13263 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13264                                      boolean ignore_defaults)
13265 {
13266   int i;
13267
13268   for (i = 0; image_config_vars[i].token != NULL; i++)
13269   {
13270     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13271
13272     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13273     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13274       continue;
13275
13276     if (value != NULL)
13277       *image_config_vars[i].value =
13278         get_token_parameter_value(image_config_vars[i].token, value);
13279   }
13280 }
13281
13282 void InitMenuDesignSettings_Static(void)
13283 {
13284   // always start with reliable default values from static default config
13285   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13286 }
13287
13288 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13289 {
13290   int i;
13291
13292   // the following initializes hierarchical values from static configuration
13293
13294   // special case: initialize "ARG_DEFAULT" values in static default config
13295   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13296   titlescreen_initial_first_default.fade_mode  =
13297     title_initial_first_default.fade_mode;
13298   titlescreen_initial_first_default.fade_delay =
13299     title_initial_first_default.fade_delay;
13300   titlescreen_initial_first_default.post_delay =
13301     title_initial_first_default.post_delay;
13302   titlescreen_initial_first_default.auto_delay =
13303     title_initial_first_default.auto_delay;
13304   titlescreen_initial_first_default.auto_delay_unit =
13305     title_initial_first_default.auto_delay_unit;
13306   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13307   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13308   titlescreen_first_default.post_delay = title_first_default.post_delay;
13309   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13310   titlescreen_first_default.auto_delay_unit =
13311     title_first_default.auto_delay_unit;
13312   titlemessage_initial_first_default.fade_mode  =
13313     title_initial_first_default.fade_mode;
13314   titlemessage_initial_first_default.fade_delay =
13315     title_initial_first_default.fade_delay;
13316   titlemessage_initial_first_default.post_delay =
13317     title_initial_first_default.post_delay;
13318   titlemessage_initial_first_default.auto_delay =
13319     title_initial_first_default.auto_delay;
13320   titlemessage_initial_first_default.auto_delay_unit =
13321     title_initial_first_default.auto_delay_unit;
13322   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13323   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13324   titlemessage_first_default.post_delay = title_first_default.post_delay;
13325   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13326   titlemessage_first_default.auto_delay_unit =
13327     title_first_default.auto_delay_unit;
13328
13329   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13330   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13331   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13332   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13333   titlescreen_initial_default.auto_delay_unit =
13334     title_initial_default.auto_delay_unit;
13335   titlescreen_default.fade_mode  = title_default.fade_mode;
13336   titlescreen_default.fade_delay = title_default.fade_delay;
13337   titlescreen_default.post_delay = title_default.post_delay;
13338   titlescreen_default.auto_delay = title_default.auto_delay;
13339   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13340   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13341   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13342   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13343   titlemessage_initial_default.auto_delay_unit =
13344     title_initial_default.auto_delay_unit;
13345   titlemessage_default.fade_mode  = title_default.fade_mode;
13346   titlemessage_default.fade_delay = title_default.fade_delay;
13347   titlemessage_default.post_delay = title_default.post_delay;
13348   titlemessage_default.auto_delay = title_default.auto_delay;
13349   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13350
13351   // special case: initialize "ARG_DEFAULT" values in static default config
13352   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13353   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13354   {
13355     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13356     titlescreen_first[i] = titlescreen_first_default;
13357     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13358     titlemessage_first[i] = titlemessage_first_default;
13359
13360     titlescreen_initial[i] = titlescreen_initial_default;
13361     titlescreen[i] = titlescreen_default;
13362     titlemessage_initial[i] = titlemessage_initial_default;
13363     titlemessage[i] = titlemessage_default;
13364   }
13365
13366   // special case: initialize "ARG_DEFAULT" values in static default config
13367   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13368   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13369   {
13370     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13371       continue;
13372
13373     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13374     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13375     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13376   }
13377
13378   // special case: initialize "ARG_DEFAULT" values in static default config
13379   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13380   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13381   {
13382     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13383     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13384     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13385
13386     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13387       continue;
13388
13389     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13390   }
13391 }
13392
13393 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13394 {
13395   static struct
13396   {
13397     struct XY *dst, *src;
13398   }
13399   game_buttons_xy[] =
13400   {
13401     { &game.button.save,        &game.button.stop       },
13402     { &game.button.pause2,      &game.button.pause      },
13403     { &game.button.load,        &game.button.play       },
13404     { &game.button.undo,        &game.button.stop       },
13405     { &game.button.redo,        &game.button.play       },
13406
13407     { NULL,                     NULL                    }
13408   };
13409   int i, j;
13410
13411   // special case: initialize later added SETUP list size from LEVELS value
13412   if (menu.list_size[GAME_MODE_SETUP] == -1)
13413     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13414
13415   // set default position for snapshot buttons to stop/pause/play buttons
13416   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13417     if ((*game_buttons_xy[i].dst).x == -1 &&
13418         (*game_buttons_xy[i].dst).y == -1)
13419       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13420
13421   // --------------------------------------------------------------------------
13422   // dynamic viewports (including playfield margins, borders and alignments)
13423   // --------------------------------------------------------------------------
13424
13425   // dynamic viewports currently only supported for landscape mode
13426   int display_width  = MAX(video.display_width, video.display_height);
13427   int display_height = MIN(video.display_width, video.display_height);
13428
13429   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13430   {
13431     struct RectWithBorder *vp_window    = &viewport.window[i];
13432     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13433     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13434     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13435     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13436     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13437     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13438     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13439
13440     // adjust window size if min/max width/height is specified
13441
13442     if (vp_window->min_width != -1)
13443     {
13444       int window_width = display_width;
13445
13446       // when using static window height, use aspect ratio of display
13447       if (vp_window->min_height == -1)
13448         window_width = vp_window->height * display_width / display_height;
13449
13450       vp_window->width = MAX(vp_window->min_width, window_width);
13451     }
13452
13453     if (vp_window->min_height != -1)
13454     {
13455       int window_height = display_height;
13456
13457       // when using static window width, use aspect ratio of display
13458       if (vp_window->min_width == -1)
13459         window_height = vp_window->width * display_height / display_width;
13460
13461       vp_window->height = MAX(vp_window->min_height, window_height);
13462     }
13463
13464     if (vp_window->max_width != -1)
13465       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13466
13467     if (vp_window->max_height != -1)
13468       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13469
13470     int playfield_width  = vp_window->width;
13471     int playfield_height = vp_window->height;
13472
13473     // adjust playfield size and position according to specified margins
13474
13475     playfield_width  -= vp_playfield->margin_left;
13476     playfield_width  -= vp_playfield->margin_right;
13477
13478     playfield_height -= vp_playfield->margin_top;
13479     playfield_height -= vp_playfield->margin_bottom;
13480
13481     // adjust playfield size if min/max width/height is specified
13482
13483     if (vp_playfield->min_width != -1)
13484       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13485
13486     if (vp_playfield->min_height != -1)
13487       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13488
13489     if (vp_playfield->max_width != -1)
13490       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13491
13492     if (vp_playfield->max_height != -1)
13493       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13494
13495     // adjust playfield position according to specified alignment
13496
13497     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13498       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13499     else if (vp_playfield->align == ALIGN_CENTER)
13500       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13501     else if (vp_playfield->align == ALIGN_RIGHT)
13502       vp_playfield->x += playfield_width - vp_playfield->width;
13503
13504     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13505       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13506     else if (vp_playfield->valign == VALIGN_MIDDLE)
13507       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13508     else if (vp_playfield->valign == VALIGN_BOTTOM)
13509       vp_playfield->y += playfield_height - vp_playfield->height;
13510
13511     vp_playfield->x += vp_playfield->margin_left;
13512     vp_playfield->y += vp_playfield->margin_top;
13513
13514     // adjust individual playfield borders if only default border is specified
13515
13516     if (vp_playfield->border_left == -1)
13517       vp_playfield->border_left = vp_playfield->border_size;
13518     if (vp_playfield->border_right == -1)
13519       vp_playfield->border_right = vp_playfield->border_size;
13520     if (vp_playfield->border_top == -1)
13521       vp_playfield->border_top = vp_playfield->border_size;
13522     if (vp_playfield->border_bottom == -1)
13523       vp_playfield->border_bottom = vp_playfield->border_size;
13524
13525     // set dynamic playfield borders if borders are specified as undefined
13526     // (but only if window size was dynamic and playfield size was static)
13527
13528     if (dynamic_window_width && !dynamic_playfield_width)
13529     {
13530       if (vp_playfield->border_left == -1)
13531       {
13532         vp_playfield->border_left = (vp_playfield->x -
13533                                      vp_playfield->margin_left);
13534         vp_playfield->x     -= vp_playfield->border_left;
13535         vp_playfield->width += vp_playfield->border_left;
13536       }
13537
13538       if (vp_playfield->border_right == -1)
13539       {
13540         vp_playfield->border_right = (vp_window->width -
13541                                       vp_playfield->x -
13542                                       vp_playfield->width -
13543                                       vp_playfield->margin_right);
13544         vp_playfield->width += vp_playfield->border_right;
13545       }
13546     }
13547
13548     if (dynamic_window_height && !dynamic_playfield_height)
13549     {
13550       if (vp_playfield->border_top == -1)
13551       {
13552         vp_playfield->border_top = (vp_playfield->y -
13553                                     vp_playfield->margin_top);
13554         vp_playfield->y      -= vp_playfield->border_top;
13555         vp_playfield->height += vp_playfield->border_top;
13556       }
13557
13558       if (vp_playfield->border_bottom == -1)
13559       {
13560         vp_playfield->border_bottom = (vp_window->height -
13561                                        vp_playfield->y -
13562                                        vp_playfield->height -
13563                                        vp_playfield->margin_bottom);
13564         vp_playfield->height += vp_playfield->border_bottom;
13565       }
13566     }
13567
13568     // adjust playfield size to be a multiple of a defined alignment tile size
13569
13570     int align_size = vp_playfield->align_size;
13571     int playfield_xtiles = vp_playfield->width  / align_size;
13572     int playfield_ytiles = vp_playfield->height / align_size;
13573     int playfield_width_corrected  = playfield_xtiles * align_size;
13574     int playfield_height_corrected = playfield_ytiles * align_size;
13575     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13576                                  i == GFX_SPECIAL_ARG_EDITOR);
13577
13578     if (is_playfield_mode &&
13579         dynamic_playfield_width &&
13580         vp_playfield->width != playfield_width_corrected)
13581     {
13582       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13583
13584       vp_playfield->width = playfield_width_corrected;
13585
13586       if (vp_playfield->align == ALIGN_LEFT)
13587       {
13588         vp_playfield->border_left += playfield_xdiff;
13589       }
13590       else if (vp_playfield->align == ALIGN_RIGHT)
13591       {
13592         vp_playfield->border_right += playfield_xdiff;
13593       }
13594       else if (vp_playfield->align == ALIGN_CENTER)
13595       {
13596         int border_left_diff  = playfield_xdiff / 2;
13597         int border_right_diff = playfield_xdiff - border_left_diff;
13598
13599         vp_playfield->border_left  += border_left_diff;
13600         vp_playfield->border_right += border_right_diff;
13601       }
13602     }
13603
13604     if (is_playfield_mode &&
13605         dynamic_playfield_height &&
13606         vp_playfield->height != playfield_height_corrected)
13607     {
13608       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13609
13610       vp_playfield->height = playfield_height_corrected;
13611
13612       if (vp_playfield->valign == VALIGN_TOP)
13613       {
13614         vp_playfield->border_top += playfield_ydiff;
13615       }
13616       else if (vp_playfield->align == VALIGN_BOTTOM)
13617       {
13618         vp_playfield->border_right += playfield_ydiff;
13619       }
13620       else if (vp_playfield->align == VALIGN_MIDDLE)
13621       {
13622         int border_top_diff    = playfield_ydiff / 2;
13623         int border_bottom_diff = playfield_ydiff - border_top_diff;
13624
13625         vp_playfield->border_top    += border_top_diff;
13626         vp_playfield->border_bottom += border_bottom_diff;
13627       }
13628     }
13629
13630     // adjust door positions according to specified alignment
13631
13632     for (j = 0; j < 2; j++)
13633     {
13634       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13635
13636       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13637         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13638       else if (vp_door->align == ALIGN_CENTER)
13639         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13640       else if (vp_door->align == ALIGN_RIGHT)
13641         vp_door->x += vp_window->width - vp_door->width;
13642
13643       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13644         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13645       else if (vp_door->valign == VALIGN_MIDDLE)
13646         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13647       else if (vp_door->valign == VALIGN_BOTTOM)
13648         vp_door->y += vp_window->height - vp_door->height;
13649     }
13650   }
13651 }
13652
13653 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13654 {
13655   static struct
13656   {
13657     struct XYTileSize *dst, *src;
13658     int graphic;
13659   }
13660   editor_buttons_xy[] =
13661   {
13662     {
13663       &editor.button.element_left,      &editor.palette.element_left,
13664       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13665     },
13666     {
13667       &editor.button.element_middle,    &editor.palette.element_middle,
13668       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13669     },
13670     {
13671       &editor.button.element_right,     &editor.palette.element_right,
13672       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13673     },
13674
13675     { NULL,                     NULL                    }
13676   };
13677   int i;
13678
13679   // set default position for element buttons to element graphics
13680   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13681   {
13682     if ((*editor_buttons_xy[i].dst).x == -1 &&
13683         (*editor_buttons_xy[i].dst).y == -1)
13684     {
13685       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13686
13687       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13688
13689       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13690     }
13691   }
13692
13693   // adjust editor palette rows and columns if specified to be dynamic
13694
13695   if (editor.palette.cols == -1)
13696   {
13697     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13698     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13699     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13700
13701     editor.palette.cols = (vp_width - sc_width) / bt_width;
13702
13703     if (editor.palette.x == -1)
13704     {
13705       int palette_width = editor.palette.cols * bt_width + sc_width;
13706
13707       editor.palette.x = (vp_width - palette_width) / 2;
13708     }
13709   }
13710
13711   if (editor.palette.rows == -1)
13712   {
13713     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13714     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13715     int tx_height = getFontHeight(FONT_TEXT_2);
13716
13717     editor.palette.rows = (vp_height - tx_height) / bt_height;
13718
13719     if (editor.palette.y == -1)
13720     {
13721       int palette_height = editor.palette.rows * bt_height + tx_height;
13722
13723       editor.palette.y = (vp_height - palette_height) / 2;
13724     }
13725   }
13726 }
13727
13728 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13729                                                       boolean initialize)
13730 {
13731   // special case: check if network and preview player positions are redefined,
13732   // to compare this later against the main menu level preview being redefined
13733   struct TokenIntPtrInfo menu_config_players[] =
13734   {
13735     { "main.network_players.x", &menu.main.network_players.redefined    },
13736     { "main.network_players.y", &menu.main.network_players.redefined    },
13737     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13738     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13739     { "preview.x",              &preview.redefined                      },
13740     { "preview.y",              &preview.redefined                      }
13741   };
13742   int i;
13743
13744   if (initialize)
13745   {
13746     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13747       *menu_config_players[i].value = FALSE;
13748   }
13749   else
13750   {
13751     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13752       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13753         *menu_config_players[i].value = TRUE;
13754   }
13755 }
13756
13757 static void InitMenuDesignSettings_PreviewPlayers(void)
13758 {
13759   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13760 }
13761
13762 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13763 {
13764   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13765 }
13766
13767 static void LoadMenuDesignSettingsFromFilename(char *filename)
13768 {
13769   static struct TitleFadingInfo tfi;
13770   static struct TitleMessageInfo tmi;
13771   static struct TokenInfo title_tokens[] =
13772   {
13773     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13774     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13775     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13776     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13777     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13778
13779     { -1,               NULL,                   NULL                    }
13780   };
13781   static struct TokenInfo titlemessage_tokens[] =
13782   {
13783     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13784     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13785     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13786     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13787     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13788     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13789     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13790     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13791     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13792     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13793     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13794     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13795     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13796     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13797     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13798     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13799     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13800     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13801
13802     { -1,               NULL,                   NULL                    }
13803   };
13804   static struct
13805   {
13806     struct TitleFadingInfo *info;
13807     char *text;
13808   }
13809   title_info[] =
13810   {
13811     // initialize first titles from "enter screen" definitions, if defined
13812     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13813     { &title_first_default,             "menu.enter_screen.TITLE"       },
13814
13815     // initialize title screens from "next screen" definitions, if defined
13816     { &title_initial_default,           "menu.next_screen.TITLE"        },
13817     { &title_default,                   "menu.next_screen.TITLE"        },
13818
13819     { NULL,                             NULL                            }
13820   };
13821   static struct
13822   {
13823     struct TitleMessageInfo *array;
13824     char *text;
13825   }
13826   titlemessage_arrays[] =
13827   {
13828     // initialize first titles from "enter screen" definitions, if defined
13829     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13830     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13831     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13832     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13833
13834     // initialize titles from "next screen" definitions, if defined
13835     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13836     { titlescreen,                      "menu.next_screen.TITLE"        },
13837     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13838     { titlemessage,                     "menu.next_screen.TITLE"        },
13839
13840     // overwrite titles with title definitions, if defined
13841     { titlescreen_initial_first,        "[title_initial]"               },
13842     { titlescreen_first,                "[title]"                       },
13843     { titlemessage_initial_first,       "[title_initial]"               },
13844     { titlemessage_first,               "[title]"                       },
13845
13846     { titlescreen_initial,              "[title_initial]"               },
13847     { titlescreen,                      "[title]"                       },
13848     { titlemessage_initial,             "[title_initial]"               },
13849     { titlemessage,                     "[title]"                       },
13850
13851     // overwrite titles with title screen/message definitions, if defined
13852     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13853     { titlescreen_first,                "[titlescreen]"                 },
13854     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13855     { titlemessage_first,               "[titlemessage]"                },
13856
13857     { titlescreen_initial,              "[titlescreen_initial]"         },
13858     { titlescreen,                      "[titlescreen]"                 },
13859     { titlemessage_initial,             "[titlemessage_initial]"        },
13860     { titlemessage,                     "[titlemessage]"                },
13861
13862     { NULL,                             NULL                            }
13863   };
13864   SetupFileHash *setup_file_hash;
13865   int i, j, k;
13866
13867   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13868     return;
13869
13870   // the following initializes hierarchical values from dynamic configuration
13871
13872   // special case: initialize with default values that may be overwritten
13873   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13874   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13875   {
13876     struct TokenIntPtrInfo menu_config[] =
13877     {
13878       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13879       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13880       { "menu.list_size",       &menu.list_size[i]      }
13881     };
13882
13883     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13884     {
13885       char *token = menu_config[j].token;
13886       char *value = getHashEntry(setup_file_hash, token);
13887
13888       if (value != NULL)
13889         *menu_config[j].value = get_integer_from_string(value);
13890     }
13891   }
13892
13893   // special case: initialize with default values that may be overwritten
13894   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13895   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13896   {
13897     struct TokenIntPtrInfo menu_config[] =
13898     {
13899       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13900       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13901       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13902       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13903       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13904     };
13905
13906     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13907     {
13908       char *token = menu_config[j].token;
13909       char *value = getHashEntry(setup_file_hash, token);
13910
13911       if (value != NULL)
13912         *menu_config[j].value = get_integer_from_string(value);
13913     }
13914   }
13915
13916   // special case: initialize with default values that may be overwritten
13917   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13918   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13919   {
13920     struct TokenIntPtrInfo menu_config[] =
13921     {
13922       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13923       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13924     };
13925
13926     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13927     {
13928       char *token = menu_config[j].token;
13929       char *value = getHashEntry(setup_file_hash, token);
13930
13931       if (value != NULL)
13932         *menu_config[j].value = get_integer_from_string(value);
13933     }
13934   }
13935
13936   // special case: initialize with default values that may be overwritten
13937   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13938   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13939   {
13940     struct TokenIntPtrInfo menu_config[] =
13941     {
13942       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13943       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13944       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13945       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13946       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13947       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13948       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13949       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13950       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13951       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13952     };
13953
13954     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13955     {
13956       char *token = menu_config[j].token;
13957       char *value = getHashEntry(setup_file_hash, token);
13958
13959       if (value != NULL)
13960         *menu_config[j].value = get_integer_from_string(value);
13961     }
13962   }
13963
13964   // special case: initialize with default values that may be overwritten
13965   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13966   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13967   {
13968     struct TokenIntPtrInfo menu_config[] =
13969     {
13970       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13971       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13972       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13973       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13974       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13975       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13976       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13977       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13978       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13979     };
13980
13981     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13982     {
13983       char *token = menu_config[j].token;
13984       char *value = getHashEntry(setup_file_hash, token);
13985
13986       if (value != NULL)
13987         *menu_config[j].value = get_token_parameter_value(token, value);
13988     }
13989   }
13990
13991   // special case: initialize with default values that may be overwritten
13992   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13993   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13994   {
13995     struct
13996     {
13997       char *token_prefix;
13998       struct RectWithBorder *struct_ptr;
13999     }
14000     vp_struct[] =
14001     {
14002       { "viewport.window",      &viewport.window[i]     },
14003       { "viewport.playfield",   &viewport.playfield[i]  },
14004       { "viewport.door_1",      &viewport.door_1[i]     },
14005       { "viewport.door_2",      &viewport.door_2[i]     }
14006     };
14007
14008     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14009     {
14010       struct TokenIntPtrInfo vp_config[] =
14011       {
14012         { ".x",                 &vp_struct[j].struct_ptr->x             },
14013         { ".y",                 &vp_struct[j].struct_ptr->y             },
14014         { ".width",             &vp_struct[j].struct_ptr->width         },
14015         { ".height",            &vp_struct[j].struct_ptr->height        },
14016         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
14017         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
14018         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
14019         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
14020         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
14021         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
14022         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
14023         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
14024         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
14025         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
14026         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
14027         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
14028         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
14029         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
14030         { ".align",             &vp_struct[j].struct_ptr->align         },
14031         { ".valign",            &vp_struct[j].struct_ptr->valign        }
14032       };
14033
14034       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14035       {
14036         char *token = getStringCat2(vp_struct[j].token_prefix,
14037                                     vp_config[k].token);
14038         char *value = getHashEntry(setup_file_hash, token);
14039
14040         if (value != NULL)
14041           *vp_config[k].value = get_token_parameter_value(token, value);
14042
14043         free(token);
14044       }
14045     }
14046   }
14047
14048   // special case: initialize with default values that may be overwritten
14049   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14050   for (i = 0; title_info[i].info != NULL; i++)
14051   {
14052     struct TitleFadingInfo *info = title_info[i].info;
14053     char *base_token = title_info[i].text;
14054
14055     for (j = 0; title_tokens[j].type != -1; j++)
14056     {
14057       char *token = getStringCat2(base_token, title_tokens[j].text);
14058       char *value = getHashEntry(setup_file_hash, token);
14059
14060       if (value != NULL)
14061       {
14062         int parameter_value = get_token_parameter_value(token, value);
14063
14064         tfi = *info;
14065
14066         *(int *)title_tokens[j].value = (int)parameter_value;
14067
14068         *info = tfi;
14069       }
14070
14071       free(token);
14072     }
14073   }
14074
14075   // special case: initialize with default values that may be overwritten
14076   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14077   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14078   {
14079     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14080     char *base_token = titlemessage_arrays[i].text;
14081
14082     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14083     {
14084       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14085       char *value = getHashEntry(setup_file_hash, token);
14086
14087       if (value != NULL)
14088       {
14089         int parameter_value = get_token_parameter_value(token, value);
14090
14091         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14092         {
14093           tmi = array[k];
14094
14095           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14096             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14097           else
14098             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14099
14100           array[k] = tmi;
14101         }
14102       }
14103
14104       free(token);
14105     }
14106   }
14107
14108   // read (and overwrite with) values that may be specified in config file
14109   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14110
14111   // special case: check if network and preview player positions are redefined
14112   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14113
14114   freeSetupFileHash(setup_file_hash);
14115 }
14116
14117 void LoadMenuDesignSettings(void)
14118 {
14119   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14120
14121   InitMenuDesignSettings_Static();
14122   InitMenuDesignSettings_SpecialPreProcessing();
14123   InitMenuDesignSettings_PreviewPlayers();
14124
14125   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14126   {
14127     // first look for special settings configured in level series config
14128     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14129
14130     if (fileExists(filename_base))
14131       LoadMenuDesignSettingsFromFilename(filename_base);
14132   }
14133
14134   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14135
14136   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14137     LoadMenuDesignSettingsFromFilename(filename_local);
14138
14139   InitMenuDesignSettings_SpecialPostProcessing();
14140 }
14141
14142 void LoadMenuDesignSettings_AfterGraphics(void)
14143 {
14144   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14145 }
14146
14147 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14148                                 boolean ignore_defaults)
14149 {
14150   int i;
14151
14152   for (i = 0; sound_config_vars[i].token != NULL; i++)
14153   {
14154     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14155
14156     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14157     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14158       continue;
14159
14160     if (value != NULL)
14161       *sound_config_vars[i].value =
14162         get_token_parameter_value(sound_config_vars[i].token, value);
14163   }
14164 }
14165
14166 void InitSoundSettings_Static(void)
14167 {
14168   // always start with reliable default values from static default config
14169   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14170 }
14171
14172 static void LoadSoundSettingsFromFilename(char *filename)
14173 {
14174   SetupFileHash *setup_file_hash;
14175
14176   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14177     return;
14178
14179   // read (and overwrite with) values that may be specified in config file
14180   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14181
14182   freeSetupFileHash(setup_file_hash);
14183 }
14184
14185 void LoadSoundSettings(void)
14186 {
14187   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14188
14189   InitSoundSettings_Static();
14190
14191   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14192   {
14193     // first look for special settings configured in level series config
14194     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14195
14196     if (fileExists(filename_base))
14197       LoadSoundSettingsFromFilename(filename_base);
14198   }
14199
14200   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14201
14202   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14203     LoadSoundSettingsFromFilename(filename_local);
14204 }
14205
14206 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14207 {
14208   char *filename = getEditorSetupFilename();
14209   SetupFileList *setup_file_list, *list;
14210   SetupFileHash *element_hash;
14211   int num_unknown_tokens = 0;
14212   int i;
14213
14214   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14215     return;
14216
14217   element_hash = newSetupFileHash();
14218
14219   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14220     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14221
14222   // determined size may be larger than needed (due to unknown elements)
14223   *num_elements = 0;
14224   for (list = setup_file_list; list != NULL; list = list->next)
14225     (*num_elements)++;
14226
14227   // add space for up to 3 more elements for padding that may be needed
14228   *num_elements += 3;
14229
14230   // free memory for old list of elements, if needed
14231   checked_free(*elements);
14232
14233   // allocate memory for new list of elements
14234   *elements = checked_malloc(*num_elements * sizeof(int));
14235
14236   *num_elements = 0;
14237   for (list = setup_file_list; list != NULL; list = list->next)
14238   {
14239     char *value = getHashEntry(element_hash, list->token);
14240
14241     if (value == NULL)          // try to find obsolete token mapping
14242     {
14243       char *mapped_token = get_mapped_token(list->token);
14244
14245       if (mapped_token != NULL)
14246       {
14247         value = getHashEntry(element_hash, mapped_token);
14248
14249         free(mapped_token);
14250       }
14251     }
14252
14253     if (value != NULL)
14254     {
14255       (*elements)[(*num_elements)++] = atoi(value);
14256     }
14257     else
14258     {
14259       if (num_unknown_tokens == 0)
14260       {
14261         Warn("---");
14262         Warn("unknown token(s) found in config file:");
14263         Warn("- config file: '%s'", filename);
14264
14265         num_unknown_tokens++;
14266       }
14267
14268       Warn("- token: '%s'", list->token);
14269     }
14270   }
14271
14272   if (num_unknown_tokens > 0)
14273     Warn("---");
14274
14275   while (*num_elements % 4)     // pad with empty elements, if needed
14276     (*elements)[(*num_elements)++] = EL_EMPTY;
14277
14278   freeSetupFileList(setup_file_list);
14279   freeSetupFileHash(element_hash);
14280
14281 #if 0
14282   for (i = 0; i < *num_elements; i++)
14283     Debug("editor", "element '%s' [%d]\n",
14284           element_info[(*elements)[i]].token_name, (*elements)[i]);
14285 #endif
14286 }
14287
14288 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14289                                                      boolean is_sound)
14290 {
14291   SetupFileHash *setup_file_hash = NULL;
14292   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14293   char *filename_music, *filename_prefix, *filename_info;
14294   struct
14295   {
14296     char *token;
14297     char **value_ptr;
14298   }
14299   token_to_value_ptr[] =
14300   {
14301     { "title_header",   &tmp_music_file_info.title_header       },
14302     { "artist_header",  &tmp_music_file_info.artist_header      },
14303     { "album_header",   &tmp_music_file_info.album_header       },
14304     { "year_header",    &tmp_music_file_info.year_header        },
14305     { "played_header",  &tmp_music_file_info.played_header      },
14306
14307     { "title",          &tmp_music_file_info.title              },
14308     { "artist",         &tmp_music_file_info.artist             },
14309     { "album",          &tmp_music_file_info.album              },
14310     { "year",           &tmp_music_file_info.year               },
14311     { "played",         &tmp_music_file_info.played             },
14312
14313     { NULL,             NULL                                    },
14314   };
14315   int i;
14316
14317   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14318                     getCustomMusicFilename(basename));
14319
14320   if (filename_music == NULL)
14321     return NULL;
14322
14323   // ---------- try to replace file extension ----------
14324
14325   filename_prefix = getStringCopy(filename_music);
14326   if (strrchr(filename_prefix, '.') != NULL)
14327     *strrchr(filename_prefix, '.') = '\0';
14328   filename_info = getStringCat2(filename_prefix, ".txt");
14329
14330   if (fileExists(filename_info))
14331     setup_file_hash = loadSetupFileHash(filename_info);
14332
14333   free(filename_prefix);
14334   free(filename_info);
14335
14336   if (setup_file_hash == NULL)
14337   {
14338     // ---------- try to add file extension ----------
14339
14340     filename_prefix = getStringCopy(filename_music);
14341     filename_info = getStringCat2(filename_prefix, ".txt");
14342
14343     if (fileExists(filename_info))
14344       setup_file_hash = loadSetupFileHash(filename_info);
14345
14346     free(filename_prefix);
14347     free(filename_info);
14348   }
14349
14350   if (setup_file_hash == NULL)
14351     return NULL;
14352
14353   // ---------- music file info found ----------
14354
14355   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14356
14357   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14358   {
14359     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14360
14361     *token_to_value_ptr[i].value_ptr =
14362       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14363   }
14364
14365   tmp_music_file_info.basename = getStringCopy(basename);
14366   tmp_music_file_info.music = music;
14367   tmp_music_file_info.is_sound = is_sound;
14368
14369   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14370   *new_music_file_info = tmp_music_file_info;
14371
14372   return new_music_file_info;
14373 }
14374
14375 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14376 {
14377   return get_music_file_info_ext(basename, music, FALSE);
14378 }
14379
14380 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14381 {
14382   return get_music_file_info_ext(basename, sound, TRUE);
14383 }
14384
14385 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14386                                      char *basename, boolean is_sound)
14387 {
14388   for (; list != NULL; list = list->next)
14389     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14390       return TRUE;
14391
14392   return FALSE;
14393 }
14394
14395 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14396 {
14397   return music_info_listed_ext(list, basename, FALSE);
14398 }
14399
14400 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14401 {
14402   return music_info_listed_ext(list, basename, TRUE);
14403 }
14404
14405 void LoadMusicInfo(void)
14406 {
14407   int num_music_noconf = getMusicListSize_NoConf();
14408   int num_music = getMusicListSize();
14409   int num_sounds = getSoundListSize();
14410   struct FileInfo *music, *sound;
14411   struct MusicFileInfo *next, **new;
14412
14413   int i;
14414
14415   while (music_file_info != NULL)
14416   {
14417     next = music_file_info->next;
14418
14419     checked_free(music_file_info->basename);
14420
14421     checked_free(music_file_info->title_header);
14422     checked_free(music_file_info->artist_header);
14423     checked_free(music_file_info->album_header);
14424     checked_free(music_file_info->year_header);
14425     checked_free(music_file_info->played_header);
14426
14427     checked_free(music_file_info->title);
14428     checked_free(music_file_info->artist);
14429     checked_free(music_file_info->album);
14430     checked_free(music_file_info->year);
14431     checked_free(music_file_info->played);
14432
14433     free(music_file_info);
14434
14435     music_file_info = next;
14436   }
14437
14438   new = &music_file_info;
14439
14440   // get (configured or unconfigured) music file info for all levels
14441   for (i = leveldir_current->first_level;
14442        i <= leveldir_current->last_level; i++)
14443   {
14444     int music_nr;
14445
14446     if (levelset.music[i] != MUS_UNDEFINED)
14447     {
14448       // get music file info for configured level music
14449       music_nr = levelset.music[i];
14450     }
14451     else if (num_music_noconf > 0)
14452     {
14453       // get music file info for unconfigured level music
14454       int level_pos = i - leveldir_current->first_level;
14455
14456       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14457     }
14458     else
14459     {
14460       continue;
14461     }
14462
14463     char *basename = getMusicInfoEntryFilename(music_nr);
14464
14465     if (basename == NULL)
14466       continue;
14467
14468     if (!music_info_listed(music_file_info, basename))
14469     {
14470       *new = get_music_file_info(basename, music_nr);
14471
14472       if (*new != NULL)
14473         new = &(*new)->next;
14474     }
14475   }
14476
14477   // get music file info for all remaining configured music files
14478   for (i = 0; i < num_music; i++)
14479   {
14480     music = getMusicListEntry(i);
14481
14482     if (music->filename == NULL)
14483       continue;
14484
14485     if (strEqual(music->filename, UNDEFINED_FILENAME))
14486       continue;
14487
14488     // a configured file may be not recognized as music
14489     if (!FileIsMusic(music->filename))
14490       continue;
14491
14492     if (!music_info_listed(music_file_info, music->filename))
14493     {
14494       *new = get_music_file_info(music->filename, i);
14495
14496       if (*new != NULL)
14497         new = &(*new)->next;
14498     }
14499   }
14500
14501   // get sound file info for all configured sound files
14502   for (i = 0; i < num_sounds; i++)
14503   {
14504     sound = getSoundListEntry(i);
14505
14506     if (sound->filename == NULL)
14507       continue;
14508
14509     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14510       continue;
14511
14512     // a configured file may be not recognized as sound
14513     if (!FileIsSound(sound->filename))
14514       continue;
14515
14516     if (!sound_info_listed(music_file_info, sound->filename))
14517     {
14518       *new = get_sound_file_info(sound->filename, i);
14519       if (*new != NULL)
14520         new = &(*new)->next;
14521     }
14522   }
14523
14524   // add pointers to previous list nodes
14525
14526   struct MusicFileInfo *node = music_file_info;
14527
14528   while (node != NULL)
14529   {
14530     if (node->next)
14531       node->next->prev = node;
14532
14533     node = node->next;
14534   }
14535 }
14536
14537 static void add_helpanim_entry(int element, int action, int direction,
14538                                int delay, int *num_list_entries)
14539 {
14540   struct HelpAnimInfo *new_list_entry;
14541   (*num_list_entries)++;
14542
14543   helpanim_info =
14544     checked_realloc(helpanim_info,
14545                     *num_list_entries * sizeof(struct HelpAnimInfo));
14546   new_list_entry = &helpanim_info[*num_list_entries - 1];
14547
14548   new_list_entry->element = element;
14549   new_list_entry->action = action;
14550   new_list_entry->direction = direction;
14551   new_list_entry->delay = delay;
14552 }
14553
14554 static void print_unknown_token(char *filename, char *token, int token_nr)
14555 {
14556   if (token_nr == 0)
14557   {
14558     Warn("---");
14559     Warn("unknown token(s) found in config file:");
14560     Warn("- config file: '%s'", filename);
14561   }
14562
14563   Warn("- token: '%s'", token);
14564 }
14565
14566 static void print_unknown_token_end(int token_nr)
14567 {
14568   if (token_nr > 0)
14569     Warn("---");
14570 }
14571
14572 void LoadHelpAnimInfo(void)
14573 {
14574   char *filename = getHelpAnimFilename();
14575   SetupFileList *setup_file_list = NULL, *list;
14576   SetupFileHash *element_hash, *action_hash, *direction_hash;
14577   int num_list_entries = 0;
14578   int num_unknown_tokens = 0;
14579   int i;
14580
14581   if (fileExists(filename))
14582     setup_file_list = loadSetupFileList(filename);
14583
14584   if (setup_file_list == NULL)
14585   {
14586     // use reliable default values from static configuration
14587     SetupFileList *insert_ptr;
14588
14589     insert_ptr = setup_file_list =
14590       newSetupFileList(helpanim_config[0].token,
14591                        helpanim_config[0].value);
14592
14593     for (i = 1; helpanim_config[i].token; i++)
14594       insert_ptr = addListEntry(insert_ptr,
14595                                 helpanim_config[i].token,
14596                                 helpanim_config[i].value);
14597   }
14598
14599   element_hash   = newSetupFileHash();
14600   action_hash    = newSetupFileHash();
14601   direction_hash = newSetupFileHash();
14602
14603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14604     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14605
14606   for (i = 0; i < NUM_ACTIONS; i++)
14607     setHashEntry(action_hash, element_action_info[i].suffix,
14608                  i_to_a(element_action_info[i].value));
14609
14610   // do not store direction index (bit) here, but direction value!
14611   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14612     setHashEntry(direction_hash, element_direction_info[i].suffix,
14613                  i_to_a(1 << element_direction_info[i].value));
14614
14615   for (list = setup_file_list; list != NULL; list = list->next)
14616   {
14617     char *element_token, *action_token, *direction_token;
14618     char *element_value, *action_value, *direction_value;
14619     int delay = atoi(list->value);
14620
14621     if (strEqual(list->token, "end"))
14622     {
14623       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14624
14625       continue;
14626     }
14627
14628     /* first try to break element into element/action/direction parts;
14629        if this does not work, also accept combined "element[.act][.dir]"
14630        elements (like "dynamite.active"), which are unique elements */
14631
14632     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14633     {
14634       element_value = getHashEntry(element_hash, list->token);
14635       if (element_value != NULL)        // element found
14636         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14637                            &num_list_entries);
14638       else
14639       {
14640         // no further suffixes found -- this is not an element
14641         print_unknown_token(filename, list->token, num_unknown_tokens++);
14642       }
14643
14644       continue;
14645     }
14646
14647     // token has format "<prefix>.<something>"
14648
14649     action_token = strchr(list->token, '.');    // suffix may be action ...
14650     direction_token = action_token;             // ... or direction
14651
14652     element_token = getStringCopy(list->token);
14653     *strchr(element_token, '.') = '\0';
14654
14655     element_value = getHashEntry(element_hash, element_token);
14656
14657     if (element_value == NULL)          // this is no element
14658     {
14659       element_value = getHashEntry(element_hash, list->token);
14660       if (element_value != NULL)        // combined element found
14661         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14662                            &num_list_entries);
14663       else
14664         print_unknown_token(filename, list->token, num_unknown_tokens++);
14665
14666       free(element_token);
14667
14668       continue;
14669     }
14670
14671     action_value = getHashEntry(action_hash, action_token);
14672
14673     if (action_value != NULL)           // action found
14674     {
14675       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14676                     &num_list_entries);
14677
14678       free(element_token);
14679
14680       continue;
14681     }
14682
14683     direction_value = getHashEntry(direction_hash, direction_token);
14684
14685     if (direction_value != NULL)        // direction found
14686     {
14687       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14688                          &num_list_entries);
14689
14690       free(element_token);
14691
14692       continue;
14693     }
14694
14695     if (strchr(action_token + 1, '.') == NULL)
14696     {
14697       // no further suffixes found -- this is not an action nor direction
14698
14699       element_value = getHashEntry(element_hash, list->token);
14700       if (element_value != NULL)        // combined element found
14701         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14702                            &num_list_entries);
14703       else
14704         print_unknown_token(filename, list->token, num_unknown_tokens++);
14705
14706       free(element_token);
14707
14708       continue;
14709     }
14710
14711     // token has format "<prefix>.<suffix>.<something>"
14712
14713     direction_token = strchr(action_token + 1, '.');
14714
14715     action_token = getStringCopy(action_token);
14716     *strchr(action_token + 1, '.') = '\0';
14717
14718     action_value = getHashEntry(action_hash, action_token);
14719
14720     if (action_value == NULL)           // this is no action
14721     {
14722       element_value = getHashEntry(element_hash, list->token);
14723       if (element_value != NULL)        // combined element found
14724         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14725                            &num_list_entries);
14726       else
14727         print_unknown_token(filename, list->token, num_unknown_tokens++);
14728
14729       free(element_token);
14730       free(action_token);
14731
14732       continue;
14733     }
14734
14735     direction_value = getHashEntry(direction_hash, direction_token);
14736
14737     if (direction_value != NULL)        // direction found
14738     {
14739       add_helpanim_entry(atoi(element_value), atoi(action_value),
14740                          atoi(direction_value), delay, &num_list_entries);
14741
14742       free(element_token);
14743       free(action_token);
14744
14745       continue;
14746     }
14747
14748     // this is no direction
14749
14750     element_value = getHashEntry(element_hash, list->token);
14751     if (element_value != NULL)          // combined element found
14752       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14753                          &num_list_entries);
14754     else
14755       print_unknown_token(filename, list->token, num_unknown_tokens++);
14756
14757     free(element_token);
14758     free(action_token);
14759   }
14760
14761   print_unknown_token_end(num_unknown_tokens);
14762
14763   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14764   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14765
14766   freeSetupFileList(setup_file_list);
14767   freeSetupFileHash(element_hash);
14768   freeSetupFileHash(action_hash);
14769   freeSetupFileHash(direction_hash);
14770
14771 #if 0
14772   for (i = 0; i < num_list_entries; i++)
14773     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14774           EL_NAME(helpanim_info[i].element),
14775           helpanim_info[i].element,
14776           helpanim_info[i].action,
14777           helpanim_info[i].direction,
14778           helpanim_info[i].delay);
14779 #endif
14780 }
14781
14782 void LoadHelpTextInfo(void)
14783 {
14784   char *filename = getHelpTextFilename();
14785   int i;
14786
14787   if (helptext_info != NULL)
14788   {
14789     freeSetupFileHash(helptext_info);
14790     helptext_info = NULL;
14791   }
14792
14793   if (fileExists(filename))
14794     helptext_info = loadSetupFileHash(filename);
14795
14796   if (helptext_info == NULL)
14797   {
14798     // use reliable default values from static configuration
14799     helptext_info = newSetupFileHash();
14800
14801     for (i = 0; helptext_config[i].token; i++)
14802       setHashEntry(helptext_info,
14803                    helptext_config[i].token,
14804                    helptext_config[i].value);
14805   }
14806
14807 #if 0
14808   BEGIN_HASH_ITERATION(helptext_info, itr)
14809   {
14810     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14811           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14812   }
14813   END_HASH_ITERATION(hash, itr)
14814 #endif
14815 }
14816
14817
14818 // ----------------------------------------------------------------------------
14819 // convert levels
14820 // ----------------------------------------------------------------------------
14821
14822 #define MAX_NUM_CONVERT_LEVELS          1000
14823
14824 void ConvertLevels(void)
14825 {
14826   static LevelDirTree *convert_leveldir = NULL;
14827   static int convert_level_nr = -1;
14828   static int num_levels_handled = 0;
14829   static int num_levels_converted = 0;
14830   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14831   int i;
14832
14833   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14834                                                global.convert_leveldir);
14835
14836   if (convert_leveldir == NULL)
14837     Fail("no such level identifier: '%s'", global.convert_leveldir);
14838
14839   leveldir_current = convert_leveldir;
14840
14841   if (global.convert_level_nr != -1)
14842   {
14843     convert_leveldir->first_level = global.convert_level_nr;
14844     convert_leveldir->last_level  = global.convert_level_nr;
14845   }
14846
14847   convert_level_nr = convert_leveldir->first_level;
14848
14849   PrintLine("=", 79);
14850   Print("Converting levels\n");
14851   PrintLine("-", 79);
14852   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14853   Print("Level series name:       '%s'\n", convert_leveldir->name);
14854   Print("Level series author:     '%s'\n", convert_leveldir->author);
14855   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14856   PrintLine("=", 79);
14857   Print("\n");
14858
14859   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14860     levels_failed[i] = FALSE;
14861
14862   while (convert_level_nr <= convert_leveldir->last_level)
14863   {
14864     char *level_filename;
14865     boolean new_level;
14866
14867     level_nr = convert_level_nr++;
14868
14869     Print("Level %03d: ", level_nr);
14870
14871     LoadLevel(level_nr);
14872     if (level.no_level_file || level.no_valid_file)
14873     {
14874       Print("(no level)\n");
14875       continue;
14876     }
14877
14878     Print("converting level ... ");
14879
14880 #if 0
14881     // special case: conversion of some EMC levels as requested by ACME
14882     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14883 #endif
14884
14885     level_filename = getDefaultLevelFilename(level_nr);
14886     new_level = !fileExists(level_filename);
14887
14888     if (new_level)
14889     {
14890       SaveLevel(level_nr);
14891
14892       num_levels_converted++;
14893
14894       Print("converted.\n");
14895     }
14896     else
14897     {
14898       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14899         levels_failed[level_nr] = TRUE;
14900
14901       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14902     }
14903
14904     num_levels_handled++;
14905   }
14906
14907   Print("\n");
14908   PrintLine("=", 79);
14909   Print("Number of levels handled: %d\n", num_levels_handled);
14910   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14911          (num_levels_handled ?
14912           num_levels_converted * 100 / num_levels_handled : 0));
14913   PrintLine("-", 79);
14914   Print("Summary (for automatic parsing by scripts):\n");
14915   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14916          convert_leveldir->identifier, num_levels_converted,
14917          num_levels_handled,
14918          (num_levels_handled ?
14919           num_levels_converted * 100 / num_levels_handled : 0));
14920
14921   if (num_levels_handled != num_levels_converted)
14922   {
14923     Print(", FAILED:");
14924     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14925       if (levels_failed[i])
14926         Print(" %03d", i);
14927   }
14928
14929   Print("\n");
14930   PrintLine("=", 79);
14931
14932   CloseAllAndExit(0);
14933 }
14934
14935
14936 // ----------------------------------------------------------------------------
14937 // create and save images for use in level sketches (raw BMP format)
14938 // ----------------------------------------------------------------------------
14939
14940 void CreateLevelSketchImages(void)
14941 {
14942   Bitmap *bitmap1;
14943   Bitmap *bitmap2;
14944   int i;
14945
14946   InitElementPropertiesGfxElement();
14947
14948   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14949   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14950
14951   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14952   {
14953     int element = getMappedElement(i);
14954     char basename1[16];
14955     char basename2[16];
14956     char *filename1;
14957     char *filename2;
14958
14959     sprintf(basename1, "%04d.bmp", i);
14960     sprintf(basename2, "%04ds.bmp", i);
14961
14962     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14963     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14964
14965     DrawSizedElement(0, 0, element, TILESIZE);
14966     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14967
14968     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14969       Fail("cannot save level sketch image file '%s'", filename1);
14970
14971     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14972     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14973
14974     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14975       Fail("cannot save level sketch image file '%s'", filename2);
14976
14977     free(filename1);
14978     free(filename2);
14979
14980     // create corresponding SQL statements (for normal and small images)
14981     if (i < 1000)
14982     {
14983       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14984       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14985     }
14986
14987     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14988     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14989
14990     // optional: create content for forum level sketch demonstration post
14991     if (options.debug)
14992       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14993   }
14994
14995   FreeBitmap(bitmap1);
14996   FreeBitmap(bitmap2);
14997
14998   if (options.debug)
14999     fprintf(stderr, "\n");
15000
15001   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15002
15003   CloseAllAndExit(0);
15004 }
15005
15006
15007 // ----------------------------------------------------------------------------
15008 // create and save images for element collecting animations (raw BMP format)
15009 // ----------------------------------------------------------------------------
15010
15011 static boolean createCollectImage(int element)
15012 {
15013   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15014 }
15015
15016 void CreateCollectElementImages(void)
15017 {
15018   int i, j;
15019   int num_steps = 8;
15020   int anim_frames = num_steps - 1;
15021   int tile_size = TILESIZE;
15022   int anim_width  = tile_size * anim_frames;
15023   int anim_height = tile_size;
15024   int num_collect_images = 0;
15025   int pos_collect_images = 0;
15026
15027   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15028     if (createCollectImage(i))
15029       num_collect_images++;
15030
15031   Info("Creating %d element collecting animation images ...",
15032        num_collect_images);
15033
15034   int dst_width  = anim_width * 2;
15035   int dst_height = anim_height * num_collect_images / 2;
15036   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15037   char *basename_bmp = "RocksCollect.bmp";
15038   char *basename_png = "RocksCollect.png";
15039   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15040   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15041   int len_filename_bmp = strlen(filename_bmp);
15042   int len_filename_png = strlen(filename_png);
15043   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15044   char cmd_convert[max_command_len];
15045
15046   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15047            filename_bmp,
15048            filename_png);
15049
15050   // force using RGBA surface for destination bitmap
15051   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15052                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15053
15054   dst_bitmap->surface =
15055     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15056
15057   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15058   {
15059     if (!createCollectImage(i))
15060       continue;
15061
15062     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15063     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15064     int graphic = el2img(i);
15065     char *token_name = element_info[i].token_name;
15066     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15067     Bitmap *src_bitmap;
15068     int src_x, src_y;
15069
15070     Info("- creating collecting image for '%s' ...", token_name);
15071
15072     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15073
15074     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15075                tile_size, tile_size, 0, 0);
15076
15077     // force using RGBA surface for temporary bitmap (using transparent black)
15078     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15079                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15080
15081     tmp_bitmap->surface =
15082       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15083
15084     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15085
15086     for (j = 0; j < anim_frames; j++)
15087     {
15088       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15089       int frame_size = frame_size_final * num_steps;
15090       int offset = (tile_size - frame_size_final) / 2;
15091       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15092
15093       while (frame_size > frame_size_final)
15094       {
15095         frame_size /= 2;
15096
15097         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15098
15099         FreeBitmap(frame_bitmap);
15100
15101         frame_bitmap = half_bitmap;
15102       }
15103
15104       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15105                        frame_size_final, frame_size_final,
15106                        dst_x + j * tile_size + offset, dst_y + offset);
15107
15108       FreeBitmap(frame_bitmap);
15109     }
15110
15111     tmp_bitmap->surface_masked = NULL;
15112
15113     FreeBitmap(tmp_bitmap);
15114
15115     pos_collect_images++;
15116   }
15117
15118   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15119     Fail("cannot save element collecting image file '%s'", filename_bmp);
15120
15121   FreeBitmap(dst_bitmap);
15122
15123   Info("Converting image file from BMP to PNG ...");
15124
15125   if (system(cmd_convert) != 0)
15126     Fail("converting image file failed");
15127
15128   unlink(filename_bmp);
15129
15130   Info("Done.");
15131
15132   CloseAllAndExit(0);
15133 }
15134
15135
15136 // ----------------------------------------------------------------------------
15137 // create and save images for custom and group elements (raw BMP format)
15138 // ----------------------------------------------------------------------------
15139
15140 void CreateCustomElementImages(char *directory)
15141 {
15142   char *src_basename = "RocksCE-template.ilbm";
15143   char *dst_basename = "RocksCE.bmp";
15144   char *src_filename = getPath2(directory, src_basename);
15145   char *dst_filename = getPath2(directory, dst_basename);
15146   Bitmap *src_bitmap;
15147   Bitmap *bitmap;
15148   int yoffset_ce = 0;
15149   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15150   int i;
15151
15152   InitVideoDefaults();
15153
15154   ReCreateBitmap(&backbuffer, video.width, video.height);
15155
15156   src_bitmap = LoadImage(src_filename);
15157
15158   bitmap = CreateBitmap(TILEX * 16 * 2,
15159                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15160                         DEFAULT_DEPTH);
15161
15162   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15163   {
15164     int x = i % 16;
15165     int y = i / 16;
15166     int ii = i + 1;
15167     int j;
15168
15169     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15170                TILEX * x, TILEY * y + yoffset_ce);
15171
15172     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15173                TILEX, TILEY,
15174                TILEX * x + TILEX * 16,
15175                TILEY * y + yoffset_ce);
15176
15177     for (j = 2; j >= 0; j--)
15178     {
15179       int c = ii % 10;
15180
15181       BlitBitmap(src_bitmap, bitmap,
15182                  TILEX + c * 7, 0, 6, 10,
15183                  TILEX * x + 6 + j * 7,
15184                  TILEY * y + 11 + yoffset_ce);
15185
15186       BlitBitmap(src_bitmap, bitmap,
15187                  TILEX + c * 8, TILEY, 6, 10,
15188                  TILEX * 16 + TILEX * x + 6 + j * 8,
15189                  TILEY * y + 10 + yoffset_ce);
15190
15191       ii /= 10;
15192     }
15193   }
15194
15195   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15196   {
15197     int x = i % 16;
15198     int y = i / 16;
15199     int ii = i + 1;
15200     int j;
15201
15202     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15203                TILEX * x, TILEY * y + yoffset_ge);
15204
15205     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15206                TILEX, TILEY,
15207                TILEX * x + TILEX * 16,
15208                TILEY * y + yoffset_ge);
15209
15210     for (j = 1; j >= 0; j--)
15211     {
15212       int c = ii % 10;
15213
15214       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15215                  TILEX * x + 6 + j * 10,
15216                  TILEY * y + 11 + yoffset_ge);
15217
15218       BlitBitmap(src_bitmap, bitmap,
15219                  TILEX + c * 8, TILEY + 12, 6, 10,
15220                  TILEX * 16 + TILEX * x + 10 + j * 8,
15221                  TILEY * y + 10 + yoffset_ge);
15222
15223       ii /= 10;
15224     }
15225   }
15226
15227   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15228     Fail("cannot save CE graphics file '%s'", dst_filename);
15229
15230   FreeBitmap(bitmap);
15231
15232   CloseAllAndExit(0);
15233 }