changed some default settings for native BD engine
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "editor.h"
23 #include "tools.h"
24 #include "tape.h"
25 #include "config.h"
26 #include "api.h"
27
28 #define ENABLE_UNUSED_CODE      0       // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
30 #define ENABLE_RESERVED_CODE    0       // reserved for later use
31
32 #define CHUNK_ID_LEN            4       // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
35
36 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
38
39 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
50
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
53
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
58
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
61
62 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
182     &li.time,                           100
183   },
184   {
185     -1,                                 SAVE_CONF_ALWAYS,
186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
187     &li.gems_needed,                    0
188   },
189   {
190     -1,                                 -1,
191     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
192     &li.random_seed,                    0
193   },
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204   {
205     -1,                                 -1,
206     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
207     &li.em_slippery_gems,               FALSE
208   },
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214   {
215     -1,                                 -1,
216     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
217     &li.can_move_into_acid_bits,        ~0      // default: everything can
218   },
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      // default: always deadly
223   },
224   {
225     -1,                                 -1,
226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
227     &li.em_explodes_by_fire,            FALSE
228   },
229   {
230     -1,                                 -1,
231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
232     &li.score[SC_TIME_BONUS],           1
233   },
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
237     &li.auto_exit_sokoban,              FALSE
238   },
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
242     &li.auto_count_gems,                FALSE
243   },
244   {
245     -1,                                 -1,
246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
247     &li.solved_by_one_player,           FALSE
248   },
249   {
250     -1,                                 -1,
251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
252     &li.time_score_base,                1
253   },
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
257     &li.rate_time_over_score,           FALSE
258   },
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
262     &li.bd_intermission,                FALSE
263   },
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
267     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
268   },
269   {
270     -1,                                 -1,
271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
272     &li.bd_pal_timing,                  FALSE
273   },
274   {
275     -1,                                 -1,
276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
277     &li.bd_cycle_delay_ms,              200
278   },
279   {
280     -1,                                 -1,
281     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
282     &li.bd_cycle_delay_c64,             0
283   },
284   {
285     -1,                                 -1,
286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
287     &li.bd_hatching_delay_cycles,       21
288   },
289   {
290     -1,                                 -1,
291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
292     &li.bd_hatching_delay_seconds,      2
293   },
294   {
295     -1,                                 -1,
296     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
297     &li.bd_line_shifting_borders,       FALSE
298   },
299   {
300     -1,                                 -1,
301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
302     &li.bd_scan_first_and_last_row,     TRUE
303   },
304   {
305     -1,                                 -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
307     &li.bd_short_explosions,            TRUE
308   },
309   {
310     -1,                                 -1,
311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
312     &li.bd_cave_random_seed_c64,        0
313   },
314   {
315     -1,                                 -1,
316     TYPE_INTEGER,                       CONF_VALUE_32_BIT(3),
317     &li.bd_color_b,                     GD_C64_COLOR(0)
318   },
319   {
320     -1,                                 -1,
321     TYPE_INTEGER,                       CONF_VALUE_32_BIT(4),
322     &li.bd_color_0,                     GD_C64_COLOR(0)
323   },
324   {
325     -1,                                 -1,
326     TYPE_INTEGER,                       CONF_VALUE_32_BIT(5),
327     &li.bd_color_1,                     GD_C64_COLOR(8)
328   },
329   {
330     -1,                                 -1,
331     TYPE_INTEGER,                       CONF_VALUE_32_BIT(6),
332     &li.bd_color_2,                     GD_C64_COLOR(11)
333   },
334   {
335     -1,                                 -1,
336     TYPE_INTEGER,                       CONF_VALUE_32_BIT(7),
337     &li.bd_color_3,                     GD_C64_COLOR(1)
338   },
339   {
340     -1,                                 -1,
341     TYPE_INTEGER,                       CONF_VALUE_32_BIT(8),
342     &li.bd_color_4,                     GD_C64_COLOR(5)
343   },
344   {
345     -1,                                 -1,
346     TYPE_INTEGER,                       CONF_VALUE_32_BIT(9),
347     &li.bd_color_5,                     GD_C64_COLOR(6)
348   },
349
350   {
351     -1,                                 -1,
352     -1,                                 -1,
353     NULL,                               -1
354   }
355 };
356
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
358 {
359   // (these values are the same for each player)
360   {
361     EL_PLAYER_1,                        -1,
362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
363     &li.block_last_field,               FALSE   // default case for EM levels
364   },
365   {
366     EL_PLAYER_1,                        -1,
367     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
368     &li.sp_block_last_field,            TRUE    // default case for SP levels
369   },
370   {
371     EL_PLAYER_1,                        -1,
372     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
373     &li.instant_relocation,             FALSE
374   },
375   {
376     EL_PLAYER_1,                        -1,
377     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
378     &li.can_pass_to_walkable,           FALSE
379   },
380   {
381     EL_PLAYER_1,                        -1,
382     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
383     &li.block_snap_field,               TRUE
384   },
385   {
386     EL_PLAYER_1,                        -1,
387     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
388     &li.continuous_snapping,            TRUE
389   },
390   {
391     EL_PLAYER_1,                        -1,
392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
393     &li.shifted_relocation,             FALSE
394   },
395   {
396     EL_PLAYER_1,                        -1,
397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
398     &li.lazy_relocation,                FALSE
399   },
400   {
401     EL_PLAYER_1,                        -1,
402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
403     &li.finish_dig_collect,             TRUE
404   },
405   {
406     EL_PLAYER_1,                        -1,
407     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
408     &li.keep_walkable_ce,               FALSE
409   },
410
411   // (these values are different for each player)
412   {
413     EL_PLAYER_1,                        -1,
414     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
415     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
416   },
417   {
418     EL_PLAYER_1,                        -1,
419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
420     &li.initial_player_gravity[0],      FALSE
421   },
422   {
423     EL_PLAYER_1,                        -1,
424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
425     &li.use_start_element[0],           FALSE
426   },
427   {
428     EL_PLAYER_1,                        -1,
429     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
430     &li.start_element[0],               EL_PLAYER_1
431   },
432   {
433     EL_PLAYER_1,                        -1,
434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
435     &li.use_artwork_element[0],         FALSE
436   },
437   {
438     EL_PLAYER_1,                        -1,
439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
440     &li.artwork_element[0],             EL_PLAYER_1
441   },
442   {
443     EL_PLAYER_1,                        -1,
444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
445     &li.use_explosion_element[0],       FALSE
446   },
447   {
448     EL_PLAYER_1,                        -1,
449     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
450     &li.explosion_element[0],           EL_PLAYER_1
451   },
452   {
453     EL_PLAYER_1,                        -1,
454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
455     &li.use_initial_inventory[0],       FALSE
456   },
457   {
458     EL_PLAYER_1,                        -1,
459     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
460     &li.initial_inventory_size[0],      1
461   },
462   {
463     EL_PLAYER_1,                        -1,
464     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
465     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
467   },
468
469   {
470     EL_PLAYER_2,                        -1,
471     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
472     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
473   },
474   {
475     EL_PLAYER_2,                        -1,
476     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
477     &li.initial_player_gravity[1],      FALSE
478   },
479   {
480     EL_PLAYER_2,                        -1,
481     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
482     &li.use_start_element[1],           FALSE
483   },
484   {
485     EL_PLAYER_2,                        -1,
486     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
487     &li.start_element[1],               EL_PLAYER_2
488   },
489   {
490     EL_PLAYER_2,                        -1,
491     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
492     &li.use_artwork_element[1],         FALSE
493   },
494   {
495     EL_PLAYER_2,                        -1,
496     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
497     &li.artwork_element[1],             EL_PLAYER_2
498   },
499   {
500     EL_PLAYER_2,                        -1,
501     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
502     &li.use_explosion_element[1],       FALSE
503   },
504   {
505     EL_PLAYER_2,                        -1,
506     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
507     &li.explosion_element[1],           EL_PLAYER_2
508   },
509   {
510     EL_PLAYER_2,                        -1,
511     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
512     &li.use_initial_inventory[1],       FALSE
513   },
514   {
515     EL_PLAYER_2,                        -1,
516     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
517     &li.initial_inventory_size[1],      1
518   },
519   {
520     EL_PLAYER_2,                        -1,
521     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
522     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
524   },
525
526   {
527     EL_PLAYER_3,                        -1,
528     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
529     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
530   },
531   {
532     EL_PLAYER_3,                        -1,
533     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
534     &li.initial_player_gravity[2],      FALSE
535   },
536   {
537     EL_PLAYER_3,                        -1,
538     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
539     &li.use_start_element[2],           FALSE
540   },
541   {
542     EL_PLAYER_3,                        -1,
543     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
544     &li.start_element[2],               EL_PLAYER_3
545   },
546   {
547     EL_PLAYER_3,                        -1,
548     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
549     &li.use_artwork_element[2],         FALSE
550   },
551   {
552     EL_PLAYER_3,                        -1,
553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
554     &li.artwork_element[2],             EL_PLAYER_3
555   },
556   {
557     EL_PLAYER_3,                        -1,
558     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
559     &li.use_explosion_element[2],       FALSE
560   },
561   {
562     EL_PLAYER_3,                        -1,
563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
564     &li.explosion_element[2],           EL_PLAYER_3
565   },
566   {
567     EL_PLAYER_3,                        -1,
568     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
569     &li.use_initial_inventory[2],       FALSE
570   },
571   {
572     EL_PLAYER_3,                        -1,
573     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
574     &li.initial_inventory_size[2],      1
575   },
576   {
577     EL_PLAYER_3,                        -1,
578     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
579     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
581   },
582
583   {
584     EL_PLAYER_4,                        -1,
585     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
586     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
587   },
588   {
589     EL_PLAYER_4,                        -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
591     &li.initial_player_gravity[3],      FALSE
592   },
593   {
594     EL_PLAYER_4,                        -1,
595     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
596     &li.use_start_element[3],           FALSE
597   },
598   {
599     EL_PLAYER_4,                        -1,
600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
601     &li.start_element[3],               EL_PLAYER_4
602   },
603   {
604     EL_PLAYER_4,                        -1,
605     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
606     &li.use_artwork_element[3],         FALSE
607   },
608   {
609     EL_PLAYER_4,                        -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
611     &li.artwork_element[3],             EL_PLAYER_4
612   },
613   {
614     EL_PLAYER_4,                        -1,
615     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
616     &li.use_explosion_element[3],       FALSE
617   },
618   {
619     EL_PLAYER_4,                        -1,
620     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
621     &li.explosion_element[3],           EL_PLAYER_4
622   },
623   {
624     EL_PLAYER_4,                        -1,
625     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
626     &li.use_initial_inventory[3],       FALSE
627   },
628   {
629     EL_PLAYER_4,                        -1,
630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
631     &li.initial_inventory_size[3],      1
632   },
633   {
634     EL_PLAYER_4,                        -1,
635     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
636     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
638   },
639
640   // (these values are only valid for BD style levels)
641   // (some values for BD style amoeba following below)
642   {
643     EL_BDX_PLAYER,                      -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BDX_PLAYER,                      -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BDX_PLAYER,                      -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BDX_PLAYER,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BDX_PLAYER,                      -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BDX_PLAYER,                      -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BDX_SAND_1,                      -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BDX_SAND_1
677   },
678
679   {
680     EL_BDX_ROCK,                        -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BDX_ROCK_FALLING
683   },
684   {
685     EL_BDX_ROCK,                        -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BDX_ROCK
688   },
689
690   {
691     EL_BDX_DIAMOND,                     -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BDX_DIAMOND,                     -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
699   },
700   {
701     EL_BDX_DIAMOND,                     -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BDX_DIAMOND
704   },
705
706   {
707     EL_BDX_FIREFLY_1,                   -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_1_explodes_to,       EL_BDX_EXPLODING_1
710   },
711
712   {
713     EL_BDX_FIREFLY_2,                   -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BDX_EXPLODING_1
716   },
717
718   {
719     EL_BDX_BUTTERFLY_1,                 -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_1_explodes_to,     EL_BDX_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BDX_BUTTERFLY_2,                 -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BDX_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BDX_STONEFLY,                    -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BDX_ROCK_GROWING_1
734   },
735
736   {
737     EL_BDX_DRAGONFLY,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BDX_EXPLODING_1
740   },
741
742   {
743     EL_BDX_DIAMOND_GROWING_5,   -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BDX_DIAMOND
746   },
747
748   {
749     EL_BDX_BOMB_EXPLODING_4,            -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BDX_WALL
752   },
753
754   {
755     EL_BDX_NITRO_PACK_EXPLODING_4,      -1,
756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
757     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
758   },
759
760   {
761     EL_BDX_EXPLODING_5,                 -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BDX_MAGIC_WALL,                  -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BDX_MAGIC_WALL,                  -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BDX_MAGIC_WALL,                  -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BDX_MAGIC_WALL,                  -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BDX_MAGIC_WALL,                  -1,
788     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_time,             999
790   },
791   {
792     EL_BDX_MAGIC_WALL,                  -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_diamond_to,       EL_BDX_ROCK_FALLING
795   },
796   {
797     EL_BDX_MAGIC_WALL,                  -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_rock_to,          EL_BDX_DIAMOND_FALLING
800   },
801   {
802     EL_BDX_MAGIC_WALL,                  -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_mega_rock_to,     EL_BDX_NITRO_PACK_FALLING
805   },
806   {
807     EL_BDX_MAGIC_WALL,                  -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nut_to,           EL_BDX_NUT_FALLING
810   },
811   {
812     EL_BDX_MAGIC_WALL,                  -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_nitro_pack_to,    EL_BDX_MEGA_ROCK_FALLING
815   },
816   {
817     EL_BDX_MAGIC_WALL,                  -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
820   },
821   {
822     EL_BDX_MAGIC_WALL,                  -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
824     &li.bd_magic_wall_flying_rock_to,   EL_BDX_FLYING_DIAMOND_FLYING
825   },
826
827   {
828     EL_BDX_CLOCK,                       -1,
829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
830     &li.bd_clock_extra_time,            30
831   },
832
833   {
834     EL_BDX_VOODOO_DOLL,                 -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.bd_voodoo_collects_diamonds,    FALSE
837   },
838   {
839     EL_BDX_VOODOO_DOLL,                 -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
841     &li.bd_voodoo_hurt_kills_player,    FALSE
842   },
843   {
844     EL_BDX_VOODOO_DOLL,                 -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
846     &li.bd_voodoo_dies_by_rock,         FALSE
847   },
848   {
849     EL_BDX_VOODOO_DOLL,                 -1,
850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
851     &li.bd_voodoo_vanish_by_explosion,  TRUE
852   },
853   {
854     EL_BDX_VOODOO_DOLL,                 -1,
855     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
856     &li.bd_voodoo_penalty_time,         30
857   },
858
859   {
860     EL_BDX_SLIME,                       -1,
861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
862     &li.bd_slime_is_predictable,        TRUE
863   },
864   {
865     EL_BDX_SLIME,                       -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
867     &li.bd_slime_permeability_rate,     100
868   },
869   {
870     EL_BDX_SLIME,                       -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
872     &li.bd_slime_permeability_bits_c64, 0
873   },
874   {
875     EL_BDX_SLIME,                       -1,
876     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
877     &li.bd_slime_random_seed_c64,       -1
878   },
879   {
880     EL_BDX_SLIME,                       -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
882     &li.bd_slime_eats_element_1,        EL_BDX_DIAMOND
883   },
884   {
885     EL_BDX_SLIME,                       -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
887     &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
888   },
889   {
890     EL_BDX_SLIME,                       -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
892     &li.bd_slime_eats_element_2,        EL_BDX_ROCK
893   },
894   {
895     EL_BDX_SLIME,                       -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
897     &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
898   },
899   {
900     EL_BDX_SLIME,                       -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
902     &li.bd_slime_eats_element_3,        EL_BDX_NUT
903   },
904   {
905     EL_BDX_SLIME,                       -1,
906     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
907     &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
908   },
909
910   {
911     EL_BDX_ACID,                        -1,
912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
913     &li.bd_acid_eats_element,           EL_BDX_SAND_1
914   },
915   {
916     EL_BDX_ACID,                        -1,
917     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
918     &li.bd_acid_spread_rate,            3
919   },
920   {
921     EL_BDX_ACID,                        -1,
922     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
923     &li.bd_acid_turns_to_element,       EL_BDX_EXPLODING_3
924   },
925
926   {
927     EL_BDX_BITER,                       -1,
928     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
929     &li.bd_biter_move_delay,            0
930   },
931   {
932     EL_BDX_BITER,                       -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_biter_eats_element,          EL_BDX_DIAMOND
935   },
936
937   {
938     EL_BDX_BLADDER,                     -1,
939     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
940     &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
941   },
942
943   {
944     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
946     &li.bd_change_expanding_wall,       FALSE
947   },
948   {
949     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
951     &li.bd_expanding_wall_looks_like,   EL_BDX_WALL
952   },
953
954   {
955     EL_BDX_REPLICATOR,                  -1,
956     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
957     &li.bd_replicators_active,          TRUE
958   },
959   {
960     EL_BDX_REPLICATOR,                  -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
962     &li.bd_replicator_create_delay,     4
963   },
964
965   {
966     EL_BDX_CONVEYOR_LEFT,               -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
968     &li.bd_conveyor_belts_active,       TRUE
969   },
970   {
971     EL_BDX_CONVEYOR_LEFT,               -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
973     &li.bd_conveyor_belts_changed,      FALSE
974   },
975
976   {
977     EL_BDX_WATER,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.bd_water_cannot_flow_down,      FALSE
980   },
981
982   {
983     EL_BDX_NUT,                         -1,
984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
985     &li.bd_nut_content,                 EL_BDX_NUT_BREAKING_1
986   },
987
988   {
989     EL_BDX_PNEUMATIC_HAMMER,            -1,
990     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
991     &li.bd_hammer_walls_break_delay,    5
992   },
993   {
994     EL_BDX_PNEUMATIC_HAMMER,            -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
996     &li.bd_hammer_walls_reappear,       FALSE
997   },
998   {
999     EL_BDX_PNEUMATIC_HAMMER,            -1,
1000     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1001     &li.bd_hammer_walls_reappear_delay, 100
1002   },
1003
1004   {
1005     EL_BDX_ROCKET_LAUNCHER,             -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1007     &li.bd_infinite_rockets,            FALSE
1008   },
1009
1010   {
1011     EL_BDX_SKELETON,                    -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1013     &li.bd_num_skeletons_needed_for_pot, 5
1014   },
1015   {
1016     EL_BDX_SKELETON,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1018     &li.bd_skeleton_worth_num_diamonds, 0
1019   },
1020
1021   {
1022     EL_BDX_CREATURE_SWITCH,             -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1024     &li.bd_creatures_start_backwards,   FALSE
1025   },
1026   {
1027     EL_BDX_CREATURE_SWITCH,             -1,
1028     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1029     &li.bd_creatures_turn_on_hatching,  FALSE
1030   },
1031   {
1032     EL_BDX_CREATURE_SWITCH,             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.bd_creatures_auto_turn_delay,   0
1035   },
1036
1037   {
1038     EL_BDX_GRAVITY_SWITCH,              -1,
1039     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1040     &li.bd_gravity_direction,           GD_MV_DOWN
1041   },
1042   {
1043     EL_BDX_GRAVITY_SWITCH,              -1,
1044     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1045     &li.bd_gravity_switch_active,       FALSE
1046   },
1047   {
1048     EL_BDX_GRAVITY_SWITCH,              -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1050     &li.bd_gravity_switch_delay,        10
1051   },
1052   {
1053     EL_BDX_GRAVITY_SWITCH,              -1,
1054     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1055     &li.bd_gravity_affects_all,         TRUE
1056   },
1057
1058   // (the following values are related to various game elements)
1059
1060   {
1061     EL_EMERALD,                         -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_EMERALD],              10
1064   },
1065
1066   {
1067     EL_DIAMOND,                         -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_DIAMOND],              10
1070   },
1071
1072   {
1073     EL_BUG,                             -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.score[SC_BUG],                  10
1076   },
1077
1078   {
1079     EL_SPACESHIP,                       -1,
1080     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1081     &li.score[SC_SPACESHIP],            10
1082   },
1083
1084   {
1085     EL_PACMAN,                          -1,
1086     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1087     &li.score[SC_PACMAN],               10
1088   },
1089
1090   {
1091     EL_NUT,                             -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1093     &li.score[SC_NUT],                  10
1094   },
1095
1096   {
1097     EL_DYNAMITE,                        -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.score[SC_DYNAMITE],             10
1100   },
1101
1102   {
1103     EL_KEY_1,                           -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.score[SC_KEY],                  10
1106   },
1107
1108   {
1109     EL_PEARL,                           -1,
1110     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1111     &li.score[SC_PEARL],                10
1112   },
1113
1114   {
1115     EL_CRYSTAL,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.score[SC_CRYSTAL],              10
1118   },
1119
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1123     &li.amoeba_content,                 EL_DIAMOND
1124   },
1125   {
1126     EL_BD_AMOEBA,                       -1,
1127     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1128     &li.amoeba_speed,                   10
1129   },
1130   {
1131     EL_BD_AMOEBA,                       -1,
1132     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1133     &li.grow_into_diggable,             TRUE
1134   },
1135
1136   {
1137     EL_BDX_AMOEBA_1,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.bd_amoeba_1_threshold_too_big,  200
1140   },
1141   {
1142     EL_BDX_AMOEBA_1,                    -1,
1143     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1144     &li.bd_amoeba_1_slow_growth_time,   200
1145   },
1146   {
1147     EL_BDX_AMOEBA_1,                    -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1149     &li.bd_amoeba_1_content_too_big,    EL_BDX_ROCK
1150   },
1151   {
1152     EL_BDX_AMOEBA_1,                    -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1154     &li.bd_amoeba_1_content_enclosed,   EL_BDX_DIAMOND
1155   },
1156   {
1157     EL_BDX_AMOEBA_1,                    -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1159     &li.bd_amoeba_1_slow_growth_rate,   3
1160   },
1161   {
1162     EL_BDX_AMOEBA_1,                    -1,
1163     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1164     &li.bd_amoeba_1_fast_growth_rate,   25
1165   },
1166   {
1167     EL_BDX_AMOEBA_1,                    -1,
1168     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1169     &li.bd_amoeba_wait_for_hatching,    FALSE
1170   },
1171   {
1172     EL_BDX_AMOEBA_1,                    -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1174     &li.bd_amoeba_start_immediately,    TRUE
1175   },
1176
1177   {
1178     EL_BDX_AMOEBA_2,                    -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1180     &li.bd_amoeba_2_threshold_too_big,  200
1181   },
1182   {
1183     EL_BDX_AMOEBA_2,                    -1,
1184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1185     &li.bd_amoeba_2_slow_growth_time,   200
1186   },
1187   {
1188     EL_BDX_AMOEBA_2,                    -1,
1189     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1190     &li.bd_amoeba_2_content_too_big,    EL_BDX_ROCK
1191   },
1192   {
1193     EL_BDX_AMOEBA_2,                    -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1195     &li.bd_amoeba_2_content_enclosed,   EL_BDX_DIAMOND
1196   },
1197   {
1198     EL_BDX_AMOEBA_2,                    -1,
1199     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1200     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1201   },
1202   {
1203     EL_BDX_AMOEBA_2,                    -1,
1204     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1205     &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1206   },
1207   {
1208     EL_BDX_AMOEBA_2,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1210     &li.bd_amoeba_2_slow_growth_rate,   3
1211   },
1212   {
1213     EL_BDX_AMOEBA_2,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1215     &li.bd_amoeba_2_fast_growth_rate,   25
1216   },
1217   {
1218     EL_BDX_AMOEBA_2,                    -1,
1219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1220     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1221   },
1222
1223   {
1224     EL_YAMYAM,                          -1,
1225     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.yamyam_content,                 EL_ROCK, NULL,
1227     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1228   },
1229   {
1230     EL_YAMYAM,                          -1,
1231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1232     &li.score[SC_YAMYAM],               10
1233   },
1234
1235   {
1236     EL_ROBOT,                           -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.score[SC_ROBOT],                10
1239   },
1240   {
1241     EL_ROBOT,                           -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1243     &li.slurp_score,                    10
1244   },
1245
1246   {
1247     EL_ROBOT_WHEEL,                     -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.time_wheel,                     10
1250   },
1251
1252   {
1253     EL_MAGIC_WALL,                      -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.time_magic_wall,                10
1256   },
1257
1258   {
1259     EL_GAME_OF_LIFE,                    -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.game_of_life[0],                2
1262   },
1263   {
1264     EL_GAME_OF_LIFE,                    -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &li.game_of_life[1],                3
1267   },
1268   {
1269     EL_GAME_OF_LIFE,                    -1,
1270     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1271     &li.game_of_life[2],                3
1272   },
1273   {
1274     EL_GAME_OF_LIFE,                    -1,
1275     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1276     &li.game_of_life[3],                3
1277   },
1278   {
1279     EL_GAME_OF_LIFE,                    -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1281     &li.use_life_bugs,                  FALSE
1282   },
1283
1284   {
1285     EL_BIOMAZE,                         -1,
1286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1287     &li.biomaze[0],                     2
1288   },
1289   {
1290     EL_BIOMAZE,                         -1,
1291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1292     &li.biomaze[1],                     3
1293   },
1294   {
1295     EL_BIOMAZE,                         -1,
1296     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1297     &li.biomaze[2],                     3
1298   },
1299   {
1300     EL_BIOMAZE,                         -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1302     &li.biomaze[3],                     3
1303   },
1304
1305   {
1306     EL_TIMEGATE_SWITCH,                 -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_timegate,                  10
1309   },
1310
1311   {
1312     EL_LIGHT_SWITCH_ACTIVE,             -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.time_light,                     10
1315   },
1316
1317   {
1318     EL_SHIELD_NORMAL,                   -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.shield_normal_time,             10
1321   },
1322   {
1323     EL_SHIELD_NORMAL,                   -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.score[SC_SHIELD],               10
1326   },
1327
1328   {
1329     EL_SHIELD_DEADLY,                   -1,
1330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1331     &li.shield_deadly_time,             10
1332   },
1333   {
1334     EL_SHIELD_DEADLY,                   -1,
1335     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1336     &li.score[SC_SHIELD],               10
1337   },
1338
1339   {
1340     EL_EXTRA_TIME,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1342     &li.extra_time,                     10
1343   },
1344   {
1345     EL_EXTRA_TIME,                      -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1347     &li.extra_time_score,               10
1348   },
1349
1350   {
1351     EL_TIME_ORB_FULL,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.time_orb_time,                  10
1354   },
1355   {
1356     EL_TIME_ORB_FULL,                   -1,
1357     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1358     &li.use_time_orb_bug,               FALSE
1359   },
1360
1361   {
1362     EL_SPRING,                          -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.use_spring_bug,                 FALSE
1365   },
1366
1367   {
1368     EL_EMC_ANDROID,                     -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.android_move_time,              10
1371   },
1372   {
1373     EL_EMC_ANDROID,                     -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1375     &li.android_clone_time,             10
1376   },
1377   {
1378     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1379     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.android_clone_element[0],       EL_EMPTY, NULL,
1381     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1382   },
1383   {
1384     EL_EMC_ANDROID,                     -1,
1385     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1386     &li.android_clone_element[0],       EL_EMPTY, NULL,
1387     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1388   },
1389
1390   {
1391     EL_EMC_LENSES,                      -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1393     &li.lenses_score,                   10
1394   },
1395   {
1396     EL_EMC_LENSES,                      -1,
1397     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1398     &li.lenses_time,                    10
1399   },
1400
1401   {
1402     EL_EMC_MAGNIFIER,                   -1,
1403     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1404     &li.magnify_score,                  10
1405   },
1406   {
1407     EL_EMC_MAGNIFIER,                   -1,
1408     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1409     &li.magnify_time,                   10
1410   },
1411
1412   {
1413     EL_EMC_MAGIC_BALL,                  -1,
1414     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1415     &li.ball_time,                      10
1416   },
1417   {
1418     EL_EMC_MAGIC_BALL,                  -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.ball_random,                    FALSE
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL,                  -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.ball_active_initial,            FALSE
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL,                  -1,
1429     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1430     &li.ball_content,                   EL_EMPTY, NULL,
1431     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1432   },
1433
1434   {
1435     EL_SOKOBAN_FIELD_EMPTY,             -1,
1436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1437     &li.sb_fields_needed,               TRUE
1438   },
1439
1440   {
1441     EL_SOKOBAN_OBJECT,                  -1,
1442     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1443     &li.sb_objects_needed,              TRUE
1444   },
1445
1446   {
1447     EL_MM_MCDUFFIN,                     -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &li.mm_laser_red,                   FALSE
1450   },
1451   {
1452     EL_MM_MCDUFFIN,                     -1,
1453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1454     &li.mm_laser_green,                 FALSE
1455   },
1456   {
1457     EL_MM_MCDUFFIN,                     -1,
1458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1459     &li.mm_laser_blue,                  TRUE
1460   },
1461
1462   {
1463     EL_DF_LASER,                        -1,
1464     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1465     &li.df_laser_red,                   TRUE
1466   },
1467   {
1468     EL_DF_LASER,                        -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1470     &li.df_laser_green,                 TRUE
1471   },
1472   {
1473     EL_DF_LASER,                        -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1475     &li.df_laser_blue,                  FALSE
1476   },
1477
1478   {
1479     EL_MM_FUSE_ACTIVE,                  -1,
1480     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1481     &li.mm_time_fuse,                   25
1482   },
1483   {
1484     EL_MM_BOMB,                         -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1486     &li.mm_time_bomb,                   75
1487   },
1488
1489   {
1490     EL_MM_GRAY_BALL,                    -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1492     &li.mm_time_ball,                   75
1493   },
1494   {
1495     EL_MM_GRAY_BALL,                    -1,
1496     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1497     &li.mm_ball_choice_mode,            ANIM_RANDOM
1498   },
1499   {
1500     EL_MM_GRAY_BALL,                    -1,
1501     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1502     &li.mm_ball_content,                EL_EMPTY, NULL,
1503     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1504   },
1505   {
1506     EL_MM_GRAY_BALL,                    -1,
1507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1508     &li.rotate_mm_ball_content,         TRUE
1509   },
1510   {
1511     EL_MM_GRAY_BALL,                    -1,
1512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1513     &li.explode_mm_ball,                FALSE
1514   },
1515
1516   {
1517     EL_MM_STEEL_BLOCK,                  -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1519     &li.mm_time_block,                  75
1520   },
1521   {
1522     EL_MM_LIGHTBALL,                    -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1524     &li.score[SC_ELEM_BONUS],           10
1525   },
1526
1527   {
1528     -1,                                 -1,
1529     -1,                                 -1,
1530     NULL,                               -1
1531   }
1532 };
1533
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1535 {
1536   {
1537     -1,                                 -1,
1538     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1539     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1544     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1550     &xx_envelope.autowrap,              FALSE
1551   },
1552   {
1553     -1,                                 -1,
1554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1555     &xx_envelope.centered,              FALSE
1556   },
1557
1558   {
1559     -1,                                 -1,
1560     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1561     &xx_envelope.text,                  -1, NULL,
1562     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1563     &xx_default_string_empty[0]
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     -1,                                 -1,
1569     NULL,                               -1
1570   }
1571 };
1572
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1574 {
1575   {
1576     -1,                                 -1,
1577     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1578     &xx_ei.description[0],              -1,
1579     &yy_ei.description[0],
1580     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1581     &xx_default_description[0]
1582   },
1583
1584   {
1585     -1,                                 -1,
1586     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1587     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1589   },
1590 #if ENABLE_RESERVED_CODE
1591   // (reserved for later use)
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1595     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1597   },
1598 #endif
1599
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1603     &xx_ei.use_gfx_element,             FALSE,
1604     &yy_ei.use_gfx_element
1605   },
1606   {
1607     -1,                                 -1,
1608     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1609     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1610     &yy_ei.gfx_element_initial
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1616     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1617     &yy_ei.access_direction
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1623     &xx_ei.collect_score_initial,       10,
1624     &yy_ei.collect_score_initial
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1629     &xx_ei.collect_count_initial,       1,
1630     &yy_ei.collect_count_initial
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1636     &xx_ei.ce_value_fixed_initial,      0,
1637     &yy_ei.ce_value_fixed_initial
1638   },
1639   {
1640     -1,                                 -1,
1641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1642     &xx_ei.ce_value_random_initial,     0,
1643     &yy_ei.ce_value_random_initial
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1648     &xx_ei.use_last_ce_value,           FALSE,
1649     &yy_ei.use_last_ce_value
1650   },
1651
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1655     &xx_ei.push_delay_fixed,            8,
1656     &yy_ei.push_delay_fixed
1657   },
1658   {
1659     -1,                                 -1,
1660     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1661     &xx_ei.push_delay_random,           8,
1662     &yy_ei.push_delay_random
1663   },
1664   {
1665     -1,                                 -1,
1666     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1667     &xx_ei.drop_delay_fixed,            0,
1668     &yy_ei.drop_delay_fixed
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1673     &xx_ei.drop_delay_random,           0,
1674     &yy_ei.drop_delay_random
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1679     &xx_ei.move_delay_fixed,            0,
1680     &yy_ei.move_delay_fixed
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1685     &xx_ei.move_delay_random,           0,
1686     &yy_ei.move_delay_random
1687   },
1688   {
1689     -1,                                 -1,
1690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1691     &xx_ei.step_delay_fixed,            0,
1692     &yy_ei.step_delay_fixed
1693   },
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1697     &xx_ei.step_delay_random,           0,
1698     &yy_ei.step_delay_random
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1704     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1705     &yy_ei.move_pattern
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1710     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1711     &yy_ei.move_direction_initial
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1716     &xx_ei.move_stepsize,               TILEX / 8,
1717     &yy_ei.move_stepsize
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1723     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1724     &yy_ei.move_enter_element
1725   },
1726   {
1727     -1,                                 -1,
1728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1729     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1730     &yy_ei.move_leave_element
1731   },
1732   {
1733     -1,                                 -1,
1734     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1735     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1736     &yy_ei.move_leave_type
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1742     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1743     &yy_ei.slippery_type
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1749     &xx_ei.explosion_type,              EXPLODES_3X3,
1750     &yy_ei.explosion_type
1751   },
1752   {
1753     -1,                                 -1,
1754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1755     &xx_ei.explosion_delay,             16,
1756     &yy_ei.explosion_delay
1757   },
1758   {
1759     -1,                                 -1,
1760     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1761     &xx_ei.ignition_delay,              8,
1762     &yy_ei.ignition_delay
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1768     &xx_ei.content,                     EL_EMPTY_SPACE,
1769     &yy_ei.content,
1770     &xx_num_contents,                   1, 1
1771   },
1772
1773   // ---------- "num_change_pages" must be the last entry ---------------------
1774
1775   {
1776     -1,                                 SAVE_CONF_ALWAYS,
1777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1778     &xx_ei.num_change_pages,            1,
1779     &yy_ei.num_change_pages
1780   },
1781
1782   {
1783     -1,                                 -1,
1784     -1,                                 -1,
1785     NULL,                               -1,
1786     NULL
1787   }
1788 };
1789
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1791 {
1792   // ---------- "current_change_page" must be the first entry -----------------
1793
1794   {
1795     -1,                                 SAVE_CONF_ALWAYS,
1796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1797     &xx_current_change_page,            -1
1798   },
1799
1800   // ---------- (the remaining entries can be in any order) -------------------
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1805     &xx_change.can_change,              FALSE
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1811     &xx_event_bits[0],                  0
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1816     &xx_event_bits[1],                  0
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1822     &xx_change.trigger_player,          CH_PLAYER_ANY
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1827     &xx_change.trigger_side,            CH_SIDE_ANY
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1832     &xx_change.trigger_page,            CH_PAGE_ANY
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1838     &xx_change.target_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1844     &xx_change.delay_fixed,             0
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1849     &xx_change.delay_random,            0
1850   },
1851   {
1852     -1,                                 -1,
1853     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1854     &xx_change.delay_frames,            FRAMES_PER_SECOND
1855   },
1856
1857   {
1858     -1,                                 -1,
1859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1860     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861   },
1862
1863   {
1864     -1,                                 -1,
1865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1866     &xx_change.explode,                 FALSE
1867   },
1868   {
1869     -1,                                 -1,
1870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1871     &xx_change.use_target_content,      FALSE
1872   },
1873   {
1874     -1,                                 -1,
1875     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1876     &xx_change.only_if_complete,        FALSE
1877   },
1878   {
1879     -1,                                 -1,
1880     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1881     &xx_change.use_random_replace,      FALSE
1882   },
1883   {
1884     -1,                                 -1,
1885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1886     &xx_change.random_percentage,       100
1887   },
1888   {
1889     -1,                                 -1,
1890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1891     &xx_change.replace_when,            CP_WHEN_EMPTY
1892   },
1893
1894   {
1895     -1,                                 -1,
1896     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1897     &xx_change.has_action,              FALSE
1898   },
1899   {
1900     -1,                                 -1,
1901     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1902     &xx_change.action_type,             CA_NO_ACTION
1903   },
1904   {
1905     -1,                                 -1,
1906     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1907     &xx_change.action_mode,             CA_MODE_UNDEFINED
1908   },
1909   {
1910     -1,                                 -1,
1911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1912     &xx_change.action_arg,              CA_ARG_UNDEFINED
1913   },
1914
1915   {
1916     -1,                                 -1,
1917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1918     &xx_change.action_element,          EL_EMPTY_SPACE
1919   },
1920
1921   {
1922     -1,                                 -1,
1923     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1924     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1925     &xx_num_contents,                   1, 1
1926   },
1927
1928   {
1929     -1,                                 -1,
1930     -1,                                 -1,
1931     NULL,                               -1
1932   }
1933 };
1934
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1936 {
1937   {
1938     -1,                                 -1,
1939     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1940     &xx_ei.description[0],              -1, NULL,
1941     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1942     &xx_default_description[0]
1943   },
1944
1945   {
1946     -1,                                 -1,
1947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1948     &xx_ei.use_gfx_element,             FALSE
1949   },
1950   {
1951     -1,                                 -1,
1952     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1953     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1954   },
1955
1956   {
1957     -1,                                 -1,
1958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1959     &xx_group.choice_mode,              ANIM_RANDOM
1960   },
1961
1962   {
1963     -1,                                 -1,
1964     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1965     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1966     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1967   },
1968
1969   {
1970     -1,                                 -1,
1971     -1,                                 -1,
1972     NULL,                               -1
1973   }
1974 };
1975
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1977 {
1978   {
1979     -1,                                 -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1981     &xx_ei.use_gfx_element,             FALSE
1982   },
1983   {
1984     -1,                                 -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1986     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1987   },
1988
1989   {
1990     -1,                                 -1,
1991     -1,                                 -1,
1992     NULL,                               -1
1993   }
1994 };
1995
1996 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1997 {
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
2001     &li.block_snap_field,               TRUE
2002   },
2003   {
2004     EL_PLAYER_1,                        -1,
2005     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2006     &li.continuous_snapping,            TRUE
2007   },
2008   {
2009     EL_PLAYER_1,                        -1,
2010     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2011     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2012   },
2013   {
2014     EL_PLAYER_1,                        -1,
2015     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2016     &li.use_start_element[0],           FALSE
2017   },
2018   {
2019     EL_PLAYER_1,                        -1,
2020     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2021     &li.start_element[0],               EL_PLAYER_1
2022   },
2023   {
2024     EL_PLAYER_1,                        -1,
2025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2026     &li.use_artwork_element[0],         FALSE
2027   },
2028   {
2029     EL_PLAYER_1,                        -1,
2030     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2031     &li.artwork_element[0],             EL_PLAYER_1
2032   },
2033   {
2034     EL_PLAYER_1,                        -1,
2035     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2036     &li.use_explosion_element[0],       FALSE
2037   },
2038   {
2039     EL_PLAYER_1,                        -1,
2040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2041     &li.explosion_element[0],           EL_PLAYER_1
2042   },
2043
2044   {
2045     -1,                                 -1,
2046     -1,                                 -1,
2047     NULL,                               -1
2048   }
2049 };
2050
2051 static struct
2052 {
2053   int filetype;
2054   char *id;
2055 }
2056 filetype_id_list[] =
2057 {
2058   { LEVEL_FILE_TYPE_RND,        "RND"   },
2059   { LEVEL_FILE_TYPE_BD,         "BD"    },
2060   { LEVEL_FILE_TYPE_EM,         "EM"    },
2061   { LEVEL_FILE_TYPE_SP,         "SP"    },
2062   { LEVEL_FILE_TYPE_DX,         "DX"    },
2063   { LEVEL_FILE_TYPE_SB,         "SB"    },
2064   { LEVEL_FILE_TYPE_DC,         "DC"    },
2065   { LEVEL_FILE_TYPE_MM,         "MM"    },
2066   { LEVEL_FILE_TYPE_MM,         "DF"    },
2067   { -1,                         NULL    },
2068 };
2069
2070
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2074
2075 static boolean check_special_flags(char *flag)
2076 {
2077   if (strEqual(options.special_flags, flag) ||
2078       strEqual(leveldir_current->special_flags, flag))
2079     return TRUE;
2080
2081   return FALSE;
2082 }
2083
2084 static struct DateInfo getCurrentDate(void)
2085 {
2086   time_t epoch_seconds = time(NULL);
2087   struct tm *now = localtime(&epoch_seconds);
2088   struct DateInfo date;
2089
2090   date.year  = now->tm_year + 1900;
2091   date.month = now->tm_mon  + 1;
2092   date.day   = now->tm_mday;
2093
2094   date.src   = DATE_SRC_CLOCK;
2095
2096   return date;
2097 }
2098
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2100 {
2101   int i;
2102
2103   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104     change->has_event[i] = FALSE;
2105 }
2106
2107 static void resetEventBits(void)
2108 {
2109   int i;
2110
2111   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112     xx_event_bits[i] = 0;
2113 }
2114
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2116 {
2117   int i;
2118
2119   /* important: only change event flag if corresponding event bit is set
2120      (this is because all xx_event_bits[] values are loaded separately,
2121      and all xx_event_bits[] values are set back to zero before loading
2122      another value xx_event_bits[x] (each value representing 32 flags)) */
2123
2124   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126       change->has_event[i] = TRUE;
2127 }
2128
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2130 {
2131   int i;
2132
2133   /* in contrast to the above function setEventFlagsFromEventBits(), it
2134      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135      depending on the corresponding change->has_event[i] values here, as
2136      all xx_event_bits[] values are reset in resetEventBits() before */
2137
2138   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139     if (change->has_event[i])
2140       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2141 }
2142
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2144 {
2145   static char description[MAX_ELEMENT_NAME_LEN + 1];
2146   char *default_description = (ei->custom_description != NULL ?
2147                                ei->custom_description :
2148                                ei->editor_description);
2149   int i;
2150
2151   // always start with reliable default values
2152   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153     description[i] = '\0';
2154
2155   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2157
2158   return &description[0];
2159 }
2160
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2162 {
2163   char *default_description = getDefaultElementDescription(ei);
2164   int i;
2165
2166   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167     ei->description[i] = default_description[i];
2168 }
2169
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2171 {
2172   int i;
2173
2174   for (i = 0; conf[i].data_type != -1; i++)
2175   {
2176     int default_value = conf[i].default_value;
2177     int data_type = conf[i].data_type;
2178     int conf_type = conf[i].conf_type;
2179     int byte_mask = conf_type & CONF_MASK_BYTES;
2180
2181     if (byte_mask == CONF_MASK_MULTI_BYTES)
2182     {
2183       int default_num_entities = conf[i].default_num_entities;
2184       int max_num_entities = conf[i].max_num_entities;
2185
2186       *(int *)(conf[i].num_entities) = default_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *default_string = conf[i].default_string;
2191         char *string = (char *)(conf[i].value);
2192
2193         strncpy(string, default_string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array = (int *)(conf[i].value);
2198         int j;
2199
2200         for (j = 0; j < max_num_entities; j++)
2201           element_array[j] = default_value;
2202       }
2203       else if (data_type == TYPE_CONTENT_LIST)
2204       {
2205         struct Content *content = (struct Content *)(conf[i].value);
2206         int c, x, y;
2207
2208         for (c = 0; c < max_num_entities; c++)
2209           for (y = 0; y < 3; y++)
2210             for (x = 0; x < 3; x++)
2211               content[c].e[x][y] = default_value;
2212       }
2213     }
2214     else        // constant size configuration data (1, 2 or 4 bytes)
2215     {
2216       if (data_type == TYPE_BOOLEAN)
2217         *(boolean *)(conf[i].value) = default_value;
2218       else
2219         *(int *)    (conf[i].value) = default_value;
2220     }
2221   }
2222 }
2223
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2225 {
2226   int i;
2227
2228   for (i = 0; conf[i].data_type != -1; i++)
2229   {
2230     int data_type = conf[i].data_type;
2231     int conf_type = conf[i].conf_type;
2232     int byte_mask = conf_type & CONF_MASK_BYTES;
2233
2234     if (byte_mask == CONF_MASK_MULTI_BYTES)
2235     {
2236       int max_num_entities = conf[i].max_num_entities;
2237
2238       if (data_type == TYPE_STRING)
2239       {
2240         char *string      = (char *)(conf[i].value);
2241         char *string_copy = (char *)(conf[i].value_copy);
2242
2243         strncpy(string_copy, string, max_num_entities);
2244       }
2245       else if (data_type == TYPE_ELEMENT_LIST)
2246       {
2247         int *element_array      = (int *)(conf[i].value);
2248         int *element_array_copy = (int *)(conf[i].value_copy);
2249         int j;
2250
2251         for (j = 0; j < max_num_entities; j++)
2252           element_array_copy[j] = element_array[j];
2253       }
2254       else if (data_type == TYPE_CONTENT_LIST)
2255       {
2256         struct Content *content      = (struct Content *)(conf[i].value);
2257         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2258         int c, x, y;
2259
2260         for (c = 0; c < max_num_entities; c++)
2261           for (y = 0; y < 3; y++)
2262             for (x = 0; x < 3; x++)
2263               content_copy[c].e[x][y] = content[c].e[x][y];
2264       }
2265     }
2266     else        // constant size configuration data (1, 2 or 4 bytes)
2267     {
2268       if (data_type == TYPE_BOOLEAN)
2269         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2270       else
2271         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2272     }
2273   }
2274 }
2275
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2277 {
2278   int i;
2279
2280   xx_ei = *ei_from;     // copy element data into temporary buffer
2281   yy_ei = *ei_to;       // copy element data into temporary buffer
2282
2283   copyConfigFromConfigList(chunk_config_CUSX_base);
2284
2285   *ei_from = xx_ei;
2286   *ei_to   = yy_ei;
2287
2288   // ---------- reinitialize and copy change pages ----------
2289
2290   ei_to->num_change_pages = ei_from->num_change_pages;
2291   ei_to->current_change_page = ei_from->current_change_page;
2292
2293   setElementChangePages(ei_to, ei_to->num_change_pages);
2294
2295   for (i = 0; i < ei_to->num_change_pages; i++)
2296     ei_to->change_page[i] = ei_from->change_page[i];
2297
2298   // ---------- copy group element info ----------
2299   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2300     *ei_to->group = *ei_from->group;
2301
2302   // mark this custom element as modified
2303   ei_to->modified_settings = TRUE;
2304 }
2305
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2307 {
2308   int change_page_size = sizeof(struct ElementChangeInfo);
2309
2310   ei->num_change_pages = MAX(1, change_pages);
2311
2312   ei->change_page =
2313     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2314
2315   if (ei->current_change_page >= ei->num_change_pages)
2316     ei->current_change_page = ei->num_change_pages - 1;
2317
2318   ei->change = &ei->change_page[ei->current_change_page];
2319 }
2320
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2322 {
2323   xx_change = *change;          // copy change data into temporary buffer
2324
2325   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2326
2327   *change = xx_change;
2328
2329   resetEventFlags(change);
2330
2331   change->direct_action = 0;
2332   change->other_action = 0;
2333
2334   change->pre_change_function = NULL;
2335   change->change_function = NULL;
2336   change->post_change_function = NULL;
2337 }
2338
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2340 {
2341   boolean add_border = FALSE;
2342   int x1 = 0;
2343   int y1 = 0;
2344   int x2 = STD_LEV_FIELDX - 1;
2345   int y2 = STD_LEV_FIELDY - 1;
2346   int i, x, y;
2347
2348   li = *level;          // copy level data into temporary buffer
2349   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350   *level = li;          // copy temporary buffer back to level data
2351
2352   setLevelInfoToDefaults_BD();
2353   setLevelInfoToDefaults_EM();
2354   setLevelInfoToDefaults_SP();
2355   setLevelInfoToDefaults_MM();
2356
2357   level->native_bd_level = &native_bd_level;
2358   level->native_em_level = &native_em_level;
2359   level->native_sp_level = &native_sp_level;
2360   level->native_mm_level = &native_mm_level;
2361
2362   level->file_version = FILE_VERSION_ACTUAL;
2363   level->game_version = GAME_VERSION_ACTUAL;
2364
2365   level->creation_date = getCurrentDate();
2366
2367   level->encoding_16bit_field  = TRUE;
2368   level->encoding_16bit_yamyam = TRUE;
2369   level->encoding_16bit_amoeba = TRUE;
2370
2371   // clear level name and level author string buffers
2372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373     level->name[i] = '\0';
2374   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375     level->author[i] = '\0';
2376
2377   // set level name and level author to default values
2378   strcpy(level->name, NAMELESS_LEVEL_NAME);
2379   strcpy(level->author, ANONYMOUS_NAME);
2380
2381   // set default game engine type
2382   level->game_engine_type = setup.default_game_engine_type;
2383
2384   // some game engines should have a default playfield with border elements
2385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2388   {
2389     add_border = TRUE;
2390     x1++;
2391     y1++;
2392     x2--;
2393     y2--;
2394   }
2395
2396   // set level playfield to playable default level with player and exit
2397   for (x = 0; x < MAX_LEV_FIELDX; x++)
2398   {
2399     for (y = 0; y < MAX_LEV_FIELDY; y++)
2400     {
2401       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402                          y == 0 || y == STD_LEV_FIELDY - 1))
2403         level->field[x][y] = getEngineElement(EL_STEELWALL);
2404       else
2405         level->field[x][y] = getEngineElement(EL_SAND);
2406     }
2407   }
2408
2409   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2411
2412   BorderElement = getEngineElement(EL_STEELWALL);
2413
2414   // detect custom elements when loading them
2415   level->file_has_custom_elements = FALSE;
2416
2417   // set random colors for BD style levels according to preferred color type
2418   SetRandomLevelColors_BD(setup.bd_default_color_type);
2419
2420   // set default color type and colors for BD style level colors
2421   SetDefaultLevelColorType_BD();
2422   SetDefaultLevelColors_BD();
2423
2424   // set all bug compatibility flags to "false" => do not emulate this bug
2425   level->use_action_after_change_bug = FALSE;
2426
2427   if (leveldir_current)
2428   {
2429     // try to determine better author name than 'anonymous'
2430     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2431     {
2432       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2434     }
2435     else
2436     {
2437       switch (LEVELCLASS(leveldir_current))
2438       {
2439         case LEVELCLASS_TUTORIAL:
2440           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2441           break;
2442
2443         case LEVELCLASS_CONTRIB:
2444           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2446           break;
2447
2448         case LEVELCLASS_PRIVATE:
2449           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2451           break;
2452
2453         default:
2454           // keep default value
2455           break;
2456       }
2457     }
2458   }
2459 }
2460
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2462 {
2463   static boolean clipboard_elements_initialized = FALSE;
2464   int i;
2465
2466   InitElementPropertiesStatic();
2467
2468   li = *level;          // copy level data into temporary buffer
2469   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470   *level = li;          // copy temporary buffer back to level data
2471
2472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473   {
2474     int element = i;
2475     struct ElementInfo *ei = &element_info[element];
2476
2477     if (element == EL_MM_GRAY_BALL)
2478     {
2479       struct LevelInfo_MM *level_mm = level->native_mm_level;
2480       int j;
2481
2482       for (j = 0; j < level->num_mm_ball_contents; j++)
2483         level->mm_ball_content[j] =
2484           map_element_MM_to_RND(level_mm->ball_content[j]);
2485     }
2486
2487     // never initialize clipboard elements after the very first time
2488     // (to be able to use clipboard elements between several levels)
2489     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2490       continue;
2491
2492     if (IS_ENVELOPE(element))
2493     {
2494       int envelope_nr = element - EL_ENVELOPE_1;
2495
2496       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2497
2498       level->envelope[envelope_nr] = xx_envelope;
2499     }
2500
2501     if (IS_CUSTOM_ELEMENT(element) ||
2502         IS_GROUP_ELEMENT(element) ||
2503         IS_INTERNAL_ELEMENT(element))
2504     {
2505       xx_ei = *ei;      // copy element data into temporary buffer
2506
2507       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508
2509       *ei = xx_ei;
2510     }
2511
2512     setElementChangePages(ei, 1);
2513     setElementChangeInfoToDefaults(ei->change);
2514
2515     if (IS_CUSTOM_ELEMENT(element) ||
2516         IS_GROUP_ELEMENT(element))
2517     {
2518       setElementDescriptionToDefault(ei);
2519
2520       ei->modified_settings = FALSE;
2521     }
2522
2523     if (IS_CUSTOM_ELEMENT(element) ||
2524         IS_INTERNAL_ELEMENT(element))
2525     {
2526       // internal values used in level editor
2527
2528       ei->access_type = 0;
2529       ei->access_layer = 0;
2530       ei->access_protected = 0;
2531       ei->walk_to_action = 0;
2532       ei->smash_targets = 0;
2533       ei->deadliness = 0;
2534
2535       ei->can_explode_by_fire = FALSE;
2536       ei->can_explode_smashed = FALSE;
2537       ei->can_explode_impact = FALSE;
2538
2539       ei->current_change_page = 0;
2540     }
2541
2542     if (IS_GROUP_ELEMENT(element) ||
2543         IS_INTERNAL_ELEMENT(element))
2544     {
2545       struct ElementGroupInfo *group;
2546
2547       // initialize memory for list of elements in group
2548       if (ei->group == NULL)
2549         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2550
2551       group = ei->group;
2552
2553       xx_group = *group;        // copy group data into temporary buffer
2554
2555       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556
2557       *group = xx_group;
2558     }
2559
2560     if (IS_EMPTY_ELEMENT(element) ||
2561         IS_INTERNAL_ELEMENT(element))
2562     {
2563       xx_ei = *ei;              // copy element data into temporary buffer
2564
2565       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2566
2567       *ei = xx_ei;
2568     }
2569   }
2570
2571   clipboard_elements_initialized = TRUE;
2572 }
2573
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575                                    boolean level_info_only,
2576                                    boolean reset_file_status)
2577 {
2578   setLevelInfoToDefaults_Level(level);
2579
2580   if (!level_info_only)
2581     setLevelInfoToDefaults_Elements(level);
2582
2583   if (reset_file_status)
2584   {
2585     level->no_valid_file = FALSE;
2586     level->no_level_file = FALSE;
2587   }
2588
2589   level->changed = FALSE;
2590 }
2591
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2593 {
2594   level_file_info->nr = 0;
2595   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596   level_file_info->packed = FALSE;
2597
2598   setString(&level_file_info->basename, NULL);
2599   setString(&level_file_info->filename, NULL);
2600 }
2601
2602 int getMappedElement_SB(int, boolean);
2603
2604 static void ActivateLevelTemplate(void)
2605 {
2606   int x, y;
2607
2608   if (check_special_flags("load_xsb_to_ces"))
2609   {
2610     // fill smaller playfields with padding "beyond border wall" elements
2611     if (level.fieldx < level_template.fieldx ||
2612         level.fieldy < level_template.fieldy)
2613     {
2614       short field[level.fieldx][level.fieldy];
2615       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2619
2620       // copy old playfield (which is smaller than the visible area)
2621       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622         field[x][y] = level.field[x][y];
2623
2624       // fill new, larger playfield with "beyond border wall" elements
2625       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626         level.field[x][y] = getMappedElement_SB('_', TRUE);
2627
2628       // copy the old playfield to the middle of the new playfield
2629       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2631
2632       level.fieldx = new_fieldx;
2633       level.fieldy = new_fieldy;
2634     }
2635   }
2636
2637   // Currently there is no special action needed to activate the template
2638   // data, because 'element_info' property settings overwrite the original
2639   // level data, while all other variables do not change.
2640
2641   // Exception: 'from_level_template' elements in the original level playfield
2642   // are overwritten with the corresponding elements at the same position in
2643   // playfield from the level template.
2644
2645   for (x = 0; x < level.fieldx; x++)
2646     for (y = 0; y < level.fieldy; y++)
2647       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648         level.field[x][y] = level_template.field[x][y];
2649
2650   if (check_special_flags("load_xsb_to_ces"))
2651   {
2652     struct LevelInfo level_backup = level;
2653
2654     // overwrite all individual level settings from template level settings
2655     level = level_template;
2656
2657     // restore level file info
2658     level.file_info = level_backup.file_info;
2659
2660     // restore playfield size
2661     level.fieldx = level_backup.fieldx;
2662     level.fieldy = level_backup.fieldy;
2663
2664     // restore playfield content
2665     for (x = 0; x < level.fieldx; x++)
2666       for (y = 0; y < level.fieldy; y++)
2667         level.field[x][y] = level_backup.field[x][y];
2668
2669     // restore name and author from individual level
2670     strcpy(level.name,   level_backup.name);
2671     strcpy(level.author, level_backup.author);
2672
2673     // restore flag "use_custom_template"
2674     level.use_custom_template = level_backup.use_custom_template;
2675   }
2676 }
2677
2678 boolean isLevelsetFilename_BD(char *filename)
2679 {
2680   return (strSuffixLower(filename, ".bd") ||
2681           strSuffixLower(filename, ".bdr") ||
2682           strSuffixLower(filename, ".brc") ||
2683           strSuffixLower(filename, ".gds"));
2684 }
2685
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2687 {
2688   // check for native BD level file extensions
2689   if (!isLevelsetFilename_BD(basename))
2690     return FALSE;
2691
2692   // check for standard single-level BD files (like "001.bd")
2693   if (strSuffixLower(basename, ".bd") &&
2694       strlen(basename) == 6 &&
2695       basename[0] >= '0' && basename[0] <= '9' &&
2696       basename[1] >= '0' && basename[1] <= '9' &&
2697       basename[2] >= '0' && basename[2] <= '9')
2698     return FALSE;
2699
2700   // this is a level package in native BD file format
2701   return TRUE;
2702 }
2703
2704 static char *getLevelFilenameFromBasename(char *basename)
2705 {
2706   static char *filename = NULL;
2707
2708   checked_free(filename);
2709
2710   filename = getPath2(getCurrentLevelDir(), basename);
2711
2712   return filename;
2713 }
2714
2715 static int getFileTypeFromBasename(char *basename)
2716 {
2717   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2718
2719   static char *filename = NULL;
2720   struct stat file_status;
2721
2722   // ---------- try to determine file type from filename ----------
2723
2724   // check for typical filename of a Supaplex level package file
2725   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726     return LEVEL_FILE_TYPE_SP;
2727
2728   // check for typical filename of a Diamond Caves II level package file
2729   if (strSuffixLower(basename, ".dc") ||
2730       strSuffixLower(basename, ".dc2"))
2731     return LEVEL_FILE_TYPE_DC;
2732
2733   // check for typical filename of a Sokoban level package file
2734   if (strSuffixLower(basename, ".xsb") &&
2735       strchr(basename, '%') == NULL)
2736     return LEVEL_FILE_TYPE_SB;
2737
2738   // check for typical filename of a Boulder Dash (GDash) level package file
2739   if (checkForPackageFromBasename_BD(basename))
2740     return LEVEL_FILE_TYPE_BD;
2741
2742   // ---------- try to determine file type from filesize ----------
2743
2744   checked_free(filename);
2745   filename = getPath2(getCurrentLevelDir(), basename);
2746
2747   if (stat(filename, &file_status) == 0)
2748   {
2749     // check for typical filesize of a Supaplex level package file
2750     if (file_status.st_size == 170496)
2751       return LEVEL_FILE_TYPE_SP;
2752   }
2753
2754   return LEVEL_FILE_TYPE_UNKNOWN;
2755 }
2756
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2758 {
2759   File *file;
2760
2761   if ((file = openFile(filename, MODE_READ)))
2762   {
2763     char chunk_name[CHUNK_ID_LEN + 1];
2764
2765     getFileChunkBE(file, chunk_name, NULL);
2766
2767     if (strEqual(chunk_name, "MMII") ||
2768         strEqual(chunk_name, "MIRR"))
2769       type = LEVEL_FILE_TYPE_MM;
2770
2771     closeFile(file);
2772   }
2773
2774   return type;
2775 }
2776
2777 static boolean checkForPackageFromBasename(char *basename)
2778 {
2779   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2781
2782   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2783 }
2784
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788
2789   if (nr < 0)
2790     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2791   else
2792     sprintf(basename, "%03d.%s", nr, extension);
2793
2794   return basename;
2795 }
2796
2797 static char *getSingleLevelBasename(int nr)
2798 {
2799   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2800 }
2801
2802 static char *getPackedLevelBasename(int type)
2803 {
2804   static char basename[MAX_FILENAME_LEN];
2805   char *directory = getCurrentLevelDir();
2806   Directory *dir;
2807   DirectoryEntry *dir_entry;
2808
2809   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2810
2811   if ((dir = openDirectory(directory)) == NULL)
2812   {
2813     Warn("cannot read current level directory '%s'", directory);
2814
2815     return basename;
2816   }
2817
2818   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2819   {
2820     char *entry_basename = dir_entry->basename;
2821     int entry_type = getFileTypeFromBasename(entry_basename);
2822
2823     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2824     {
2825       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2826           type == entry_type)
2827       {
2828         strcpy(basename, entry_basename);
2829
2830         break;
2831       }
2832     }
2833   }
2834
2835   closeDirectory(dir);
2836
2837   return basename;
2838 }
2839
2840 static char *getSingleLevelFilename(int nr)
2841 {
2842   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2843 }
2844
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2847 {
2848   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2849 }
2850 #endif
2851
2852 char *getDefaultLevelFilename(int nr)
2853 {
2854   return getSingleLevelFilename(nr);
2855 }
2856
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2859                                                  int type)
2860 {
2861   lfi->type = type;
2862   lfi->packed = FALSE;
2863
2864   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2866 }
2867 #endif
2868
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870                                                  int type, char *format, ...)
2871 {
2872   static char basename[MAX_FILENAME_LEN];
2873   va_list ap;
2874
2875   va_start(ap, format);
2876   vsprintf(basename, format, ap);
2877   va_end(ap);
2878
2879   lfi->type = type;
2880   lfi->packed = FALSE;
2881
2882   setString(&lfi->basename, basename);
2883   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2884 }
2885
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887                                                  int type)
2888 {
2889   lfi->type = type;
2890   lfi->packed = TRUE;
2891
2892   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2894 }
2895
2896 static int getFiletypeFromID(char *filetype_id)
2897 {
2898   char *filetype_id_lower;
2899   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2900   int i;
2901
2902   if (filetype_id == NULL)
2903     return LEVEL_FILE_TYPE_UNKNOWN;
2904
2905   filetype_id_lower = getStringToLower(filetype_id);
2906
2907   for (i = 0; filetype_id_list[i].id != NULL; i++)
2908   {
2909     char *id_lower = getStringToLower(filetype_id_list[i].id);
2910     
2911     if (strEqual(filetype_id_lower, id_lower))
2912       filetype = filetype_id_list[i].filetype;
2913
2914     free(id_lower);
2915
2916     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2917       break;
2918   }
2919
2920   free(filetype_id_lower);
2921
2922   return filetype;
2923 }
2924
2925 char *getLocalLevelTemplateFilename(void)
2926 {
2927   return getDefaultLevelFilename(-1);
2928 }
2929
2930 char *getGlobalLevelTemplateFilename(void)
2931 {
2932   // global variable "leveldir_current" must be modified in the loop below
2933   LevelDirTree *leveldir_current_last = leveldir_current;
2934   char *filename = NULL;
2935
2936   // check for template level in path from current to topmost tree node
2937
2938   while (leveldir_current != NULL)
2939   {
2940     filename = getDefaultLevelFilename(-1);
2941
2942     if (fileExists(filename))
2943       break;
2944
2945     leveldir_current = leveldir_current->node_parent;
2946   }
2947
2948   // restore global variable "leveldir_current" modified in above loop
2949   leveldir_current = leveldir_current_last;
2950
2951   return filename;
2952 }
2953
2954 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2955 {
2956   int nr = lfi->nr;
2957
2958   // special case: level number is negative => check for level template file
2959   if (nr < 0)
2960   {
2961     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2962                                          getSingleLevelBasename(-1));
2963
2964     // replace local level template filename with global template filename
2965     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2966
2967     // no fallback if template file not existing
2968     return;
2969   }
2970
2971   // special case: check for file name/pattern specified in "levelinfo.conf"
2972   if (leveldir_current->level_filename != NULL)
2973   {
2974     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2975
2976     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2977                                          leveldir_current->level_filename, nr);
2978
2979     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2980
2981     if (fileExists(lfi->filename))
2982       return;
2983   }
2984   else if (leveldir_current->level_filetype != NULL)
2985   {
2986     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2987
2988     // check for specified native level file with standard file name
2989     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2990                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2991     if (fileExists(lfi->filename))
2992       return;
2993   }
2994
2995   // check for native Rocks'n'Diamonds level file
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2997                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2998   if (fileExists(lfi->filename))
2999     return;
3000
3001   // check for native Boulder Dash level file
3002   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
3003   if (fileExists(lfi->filename))
3004     return;
3005
3006   // check for Emerald Mine level file (V1)
3007   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3008                                        'a' + (nr / 10) % 26, '0' + nr % 10);
3009   if (fileExists(lfi->filename))
3010     return;
3011   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3012                                        'A' + (nr / 10) % 26, '0' + nr % 10);
3013   if (fileExists(lfi->filename))
3014     return;
3015
3016   // check for Emerald Mine level file (V2 to V5)
3017   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3018   if (fileExists(lfi->filename))
3019     return;
3020
3021   // check for Emerald Mine level file (V6 / single mode)
3022   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3023   if (fileExists(lfi->filename))
3024     return;
3025   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3026   if (fileExists(lfi->filename))
3027     return;
3028
3029   // check for Emerald Mine level file (V6 / teamwork mode)
3030   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3031   if (fileExists(lfi->filename))
3032     return;
3033   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3034   if (fileExists(lfi->filename))
3035     return;
3036
3037   // check for various packed level file formats
3038   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3039   if (fileExists(lfi->filename))
3040     return;
3041
3042   // no known level file found -- use default values (and fail later)
3043   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3044                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3045 }
3046
3047 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3048 {
3049   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3050     lfi->type = getFileTypeFromBasename(lfi->basename);
3051
3052   if (lfi->type == LEVEL_FILE_TYPE_RND)
3053     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3054 }
3055
3056 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3057 {
3058   // always start with reliable default values
3059   setFileInfoToDefaults(level_file_info);
3060
3061   level_file_info->nr = nr;     // set requested level number
3062
3063   determineLevelFileInfo_Filename(level_file_info);
3064   determineLevelFileInfo_Filetype(level_file_info);
3065 }
3066
3067 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3068                               struct LevelFileInfo *lfi_to)
3069 {
3070   lfi_to->nr     = lfi_from->nr;
3071   lfi_to->type   = lfi_from->type;
3072   lfi_to->packed = lfi_from->packed;
3073
3074   setString(&lfi_to->basename, lfi_from->basename);
3075   setString(&lfi_to->filename, lfi_from->filename);
3076 }
3077
3078 // ----------------------------------------------------------------------------
3079 // functions for loading R'n'D level
3080 // ----------------------------------------------------------------------------
3081
3082 int getMappedElement(int element)
3083 {
3084   // remap some (historic, now obsolete) elements
3085
3086   switch (element)
3087   {
3088     case EL_PLAYER_OBSOLETE:
3089       element = EL_PLAYER_1;
3090       break;
3091
3092     case EL_KEY_OBSOLETE:
3093       element = EL_KEY_1;
3094       break;
3095
3096     case EL_EM_KEY_1_FILE_OBSOLETE:
3097       element = EL_EM_KEY_1;
3098       break;
3099
3100     case EL_EM_KEY_2_FILE_OBSOLETE:
3101       element = EL_EM_KEY_2;
3102       break;
3103
3104     case EL_EM_KEY_3_FILE_OBSOLETE:
3105       element = EL_EM_KEY_3;
3106       break;
3107
3108     case EL_EM_KEY_4_FILE_OBSOLETE:
3109       element = EL_EM_KEY_4;
3110       break;
3111
3112     case EL_ENVELOPE_OBSOLETE:
3113       element = EL_ENVELOPE_1;
3114       break;
3115
3116     case EL_SP_EMPTY:
3117       element = EL_EMPTY;
3118       break;
3119
3120     default:
3121       if (element >= NUM_FILE_ELEMENTS)
3122       {
3123         Warn("invalid level element %d", element);
3124
3125         element = EL_UNKNOWN;
3126       }
3127       break;
3128   }
3129
3130   return element;
3131 }
3132
3133 static int getMappedElementByVersion(int element, int game_version)
3134 {
3135   // remap some elements due to certain game version
3136
3137   if (game_version <= VERSION_IDENT(2,2,0,0))
3138   {
3139     // map game font elements
3140     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3141                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3142                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3143                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3144   }
3145
3146   if (game_version < VERSION_IDENT(3,0,0,0))
3147   {
3148     // map Supaplex gravity tube elements
3149     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3150                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3151                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3152                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3153                element);
3154   }
3155
3156   return element;
3157 }
3158
3159 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3160 {
3161   level->file_version = getFileVersion(file);
3162   level->game_version = getFileVersion(file);
3163
3164   return chunk_size;
3165 }
3166
3167 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3168 {
3169   level->creation_date.year  = getFile16BitBE(file);
3170   level->creation_date.month = getFile8Bit(file);
3171   level->creation_date.day   = getFile8Bit(file);
3172
3173   level->creation_date.src   = DATE_SRC_LEVELFILE;
3174
3175   return chunk_size;
3176 }
3177
3178 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3179 {
3180   int initial_player_stepsize;
3181   int initial_player_gravity;
3182   int i, x, y;
3183
3184   level->fieldx = getFile8Bit(file);
3185   level->fieldy = getFile8Bit(file);
3186
3187   level->time           = getFile16BitBE(file);
3188   level->gems_needed    = getFile16BitBE(file);
3189
3190   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3191     level->name[i] = getFile8Bit(file);
3192   level->name[MAX_LEVEL_NAME_LEN] = 0;
3193
3194   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3195     level->score[i] = getFile8Bit(file);
3196
3197   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3198   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3199     for (y = 0; y < 3; y++)
3200       for (x = 0; x < 3; x++)
3201         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3202
3203   level->amoeba_speed           = getFile8Bit(file);
3204   level->time_magic_wall        = getFile8Bit(file);
3205   level->time_wheel             = getFile8Bit(file);
3206   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3207
3208   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3209                                    STEPSIZE_NORMAL);
3210
3211   for (i = 0; i < MAX_PLAYERS; i++)
3212     level->initial_player_stepsize[i] = initial_player_stepsize;
3213
3214   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215
3216   for (i = 0; i < MAX_PLAYERS; i++)
3217     level->initial_player_gravity[i] = initial_player_gravity;
3218
3219   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221
3222   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223
3224   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3226   level->can_move_into_acid_bits = getFile32BitBE(file);
3227   level->dont_collide_with_bits = getFile8Bit(file);
3228
3229   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3230   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3231
3232   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3233   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3234   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3235
3236   level->game_engine_type       = getFile8Bit(file);
3237
3238   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3239
3240   return chunk_size;
3241 }
3242
3243 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3244 {
3245   int i;
3246
3247   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3248     level->name[i] = getFile8Bit(file);
3249   level->name[MAX_LEVEL_NAME_LEN] = 0;
3250
3251   return chunk_size;
3252 }
3253
3254 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3255 {
3256   int i;
3257
3258   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3259     level->author[i] = getFile8Bit(file);
3260   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3261
3262   return chunk_size;
3263 }
3264
3265 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3266 {
3267   int x, y;
3268   int chunk_size_expected = level->fieldx * level->fieldy;
3269
3270   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3271      stored with 16-bit encoding (and should be twice as big then).
3272      Even worse, playfield data was stored 16-bit when only yamyam content
3273      contained 16-bit elements and vice versa. */
3274
3275   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3276     chunk_size_expected *= 2;
3277
3278   if (chunk_size_expected != chunk_size)
3279   {
3280     ReadUnusedBytesFromFile(file, chunk_size);
3281     return chunk_size_expected;
3282   }
3283
3284   for (y = 0; y < level->fieldy; y++)
3285     for (x = 0; x < level->fieldx; x++)
3286       level->field[x][y] =
3287         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3288                          getFile8Bit(file));
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i, x, y;
3295   int header_size = 4;
3296   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3297   int chunk_size_expected = header_size + content_size;
3298
3299   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3300      stored with 16-bit encoding (and should be twice as big then).
3301      Even worse, playfield data was stored 16-bit when only yamyam content
3302      contained 16-bit elements and vice versa. */
3303
3304   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3305     chunk_size_expected += content_size;
3306
3307   if (chunk_size_expected != chunk_size)
3308   {
3309     ReadUnusedBytesFromFile(file, chunk_size);
3310     return chunk_size_expected;
3311   }
3312
3313   getFile8Bit(file);
3314   level->num_yamyam_contents = getFile8Bit(file);
3315   getFile8Bit(file);
3316   getFile8Bit(file);
3317
3318   // correct invalid number of content fields -- should never happen
3319   if (level->num_yamyam_contents < 1 ||
3320       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3321     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3322
3323   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3324     for (y = 0; y < 3; y++)
3325       for (x = 0; x < 3; x++)
3326         level->yamyam_content[i].e[x][y] =
3327           getMappedElement(level->encoding_16bit_field ?
3328                            getFile16BitBE(file) : getFile8Bit(file));
3329   return chunk_size;
3330 }
3331
3332 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3333 {
3334   int i, x, y;
3335   int element;
3336   int num_contents;
3337   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3338
3339   element = getMappedElement(getFile16BitBE(file));
3340   num_contents = getFile8Bit(file);
3341
3342   getFile8Bit(file);    // content x size (unused)
3343   getFile8Bit(file);    // content y size (unused)
3344
3345   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3346
3347   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3348     for (y = 0; y < 3; y++)
3349       for (x = 0; x < 3; x++)
3350         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3351
3352   // correct invalid number of content fields -- should never happen
3353   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3354     num_contents = STD_ELEMENT_CONTENTS;
3355
3356   if (element == EL_YAMYAM)
3357   {
3358     level->num_yamyam_contents = num_contents;
3359
3360     for (i = 0; i < num_contents; i++)
3361       for (y = 0; y < 3; y++)
3362         for (x = 0; x < 3; x++)
3363           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3364   }
3365   else if (element == EL_BD_AMOEBA)
3366   {
3367     level->amoeba_content = content_array[0][0][0];
3368   }
3369   else
3370   {
3371     Warn("cannot load content for element '%d'", element);
3372   }
3373
3374   return chunk_size;
3375 }
3376
3377 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3378 {
3379   int i;
3380   int element;
3381   int envelope_nr;
3382   int envelope_len;
3383   int chunk_size_expected;
3384
3385   element = getMappedElement(getFile16BitBE(file));
3386   if (!IS_ENVELOPE(element))
3387     element = EL_ENVELOPE_1;
3388
3389   envelope_nr = element - EL_ENVELOPE_1;
3390
3391   envelope_len = getFile16BitBE(file);
3392
3393   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3394   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3395
3396   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3397
3398   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3399   if (chunk_size_expected != chunk_size)
3400   {
3401     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3402     return chunk_size_expected;
3403   }
3404
3405   for (i = 0; i < envelope_len; i++)
3406     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3407
3408   return chunk_size;
3409 }
3410
3411 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3412 {
3413   int num_changed_custom_elements = getFile16BitBE(file);
3414   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3415   int i;
3416
3417   if (chunk_size_expected != chunk_size)
3418   {
3419     ReadUnusedBytesFromFile(file, chunk_size - 2);
3420     return chunk_size_expected;
3421   }
3422
3423   for (i = 0; i < num_changed_custom_elements; i++)
3424   {
3425     int element = getMappedElement(getFile16BitBE(file));
3426     int properties = getFile32BitBE(file);
3427
3428     if (IS_CUSTOM_ELEMENT(element))
3429       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3430     else
3431       Warn("invalid custom element number %d", element);
3432
3433     // older game versions that wrote level files with CUS1 chunks used
3434     // different default push delay values (not yet stored in level file)
3435     element_info[element].push_delay_fixed = 2;
3436     element_info[element].push_delay_random = 8;
3437   }
3438
3439   level->file_has_custom_elements = TRUE;
3440
3441   return chunk_size;
3442 }
3443
3444 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3445 {
3446   int num_changed_custom_elements = getFile16BitBE(file);
3447   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3448   int i;
3449
3450   if (chunk_size_expected != chunk_size)
3451   {
3452     ReadUnusedBytesFromFile(file, chunk_size - 2);
3453     return chunk_size_expected;
3454   }
3455
3456   for (i = 0; i < num_changed_custom_elements; i++)
3457   {
3458     int element = getMappedElement(getFile16BitBE(file));
3459     int custom_target_element = getMappedElement(getFile16BitBE(file));
3460
3461     if (IS_CUSTOM_ELEMENT(element))
3462       element_info[element].change->target_element = custom_target_element;
3463     else
3464       Warn("invalid custom element number %d", element);
3465   }
3466
3467   level->file_has_custom_elements = TRUE;
3468
3469   return chunk_size;
3470 }
3471
3472 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3473 {
3474   int num_changed_custom_elements = getFile16BitBE(file);
3475   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3476   int i, j, x, y;
3477
3478   if (chunk_size_expected != chunk_size)
3479   {
3480     ReadUnusedBytesFromFile(file, chunk_size - 2);
3481     return chunk_size_expected;
3482   }
3483
3484   for (i = 0; i < num_changed_custom_elements; i++)
3485   {
3486     int element = getMappedElement(getFile16BitBE(file));
3487     struct ElementInfo *ei = &element_info[element];
3488     unsigned int event_bits;
3489
3490     if (!IS_CUSTOM_ELEMENT(element))
3491     {
3492       Warn("invalid custom element number %d", element);
3493
3494       element = EL_INTERNAL_DUMMY;
3495     }
3496
3497     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3498       ei->description[j] = getFile8Bit(file);
3499     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3500
3501     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3502
3503     // some free bytes for future properties and padding
3504     ReadUnusedBytesFromFile(file, 7);
3505
3506     ei->use_gfx_element = getFile8Bit(file);
3507     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3508
3509     ei->collect_score_initial = getFile8Bit(file);
3510     ei->collect_count_initial = getFile8Bit(file);
3511
3512     ei->push_delay_fixed = getFile16BitBE(file);
3513     ei->push_delay_random = getFile16BitBE(file);
3514     ei->move_delay_fixed = getFile16BitBE(file);
3515     ei->move_delay_random = getFile16BitBE(file);
3516
3517     ei->move_pattern = getFile16BitBE(file);
3518     ei->move_direction_initial = getFile8Bit(file);
3519     ei->move_stepsize = getFile8Bit(file);
3520
3521     for (y = 0; y < 3; y++)
3522       for (x = 0; x < 3; x++)
3523         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3524
3525     // bits 0 - 31 of "has_event[]"
3526     event_bits = getFile32BitBE(file);
3527     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3528       if (event_bits & (1u << j))
3529         ei->change->has_event[j] = TRUE;
3530
3531     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3532
3533     ei->change->delay_fixed = getFile16BitBE(file);
3534     ei->change->delay_random = getFile16BitBE(file);
3535     ei->change->delay_frames = getFile16BitBE(file);
3536
3537     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3538
3539     ei->change->explode = getFile8Bit(file);
3540     ei->change->use_target_content = getFile8Bit(file);
3541     ei->change->only_if_complete = getFile8Bit(file);
3542     ei->change->use_random_replace = getFile8Bit(file);
3543
3544     ei->change->random_percentage = getFile8Bit(file);
3545     ei->change->replace_when = getFile8Bit(file);
3546
3547     for (y = 0; y < 3; y++)
3548       for (x = 0; x < 3; x++)
3549         ei->change->target_content.e[x][y] =
3550           getMappedElement(getFile16BitBE(file));
3551
3552     ei->slippery_type = getFile8Bit(file);
3553
3554     // some free bytes for future properties and padding
3555     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3556
3557     // mark that this custom element has been modified
3558     ei->modified_settings = TRUE;
3559   }
3560
3561   level->file_has_custom_elements = TRUE;
3562
3563   return chunk_size;
3564 }
3565
3566 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3567 {
3568   struct ElementInfo *ei;
3569   int chunk_size_expected;
3570   int element;
3571   int i, j, x, y;
3572
3573   // ---------- custom element base property values (96 bytes) ----------------
3574
3575   element = getMappedElement(getFile16BitBE(file));
3576
3577   if (!IS_CUSTOM_ELEMENT(element))
3578   {
3579     Warn("invalid custom element number %d", element);
3580
3581     ReadUnusedBytesFromFile(file, chunk_size - 2);
3582
3583     return chunk_size;
3584   }
3585
3586   ei = &element_info[element];
3587
3588   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3589     ei->description[i] = getFile8Bit(file);
3590   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3591
3592   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3593
3594   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3595
3596   ei->num_change_pages = getFile8Bit(file);
3597
3598   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3599   if (chunk_size_expected != chunk_size)
3600   {
3601     ReadUnusedBytesFromFile(file, chunk_size - 43);
3602     return chunk_size_expected;
3603   }
3604
3605   ei->ce_value_fixed_initial = getFile16BitBE(file);
3606   ei->ce_value_random_initial = getFile16BitBE(file);
3607   ei->use_last_ce_value = getFile8Bit(file);
3608
3609   ei->use_gfx_element = getFile8Bit(file);
3610   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3611
3612   ei->collect_score_initial = getFile8Bit(file);
3613   ei->collect_count_initial = getFile8Bit(file);
3614
3615   ei->drop_delay_fixed = getFile8Bit(file);
3616   ei->push_delay_fixed = getFile8Bit(file);
3617   ei->drop_delay_random = getFile8Bit(file);
3618   ei->push_delay_random = getFile8Bit(file);
3619   ei->move_delay_fixed = getFile16BitBE(file);
3620   ei->move_delay_random = getFile16BitBE(file);
3621
3622   // bits 0 - 15 of "move_pattern" ...
3623   ei->move_pattern = getFile16BitBE(file);
3624   ei->move_direction_initial = getFile8Bit(file);
3625   ei->move_stepsize = getFile8Bit(file);
3626
3627   ei->slippery_type = getFile8Bit(file);
3628
3629   for (y = 0; y < 3; y++)
3630     for (x = 0; x < 3; x++)
3631       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3632
3633   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3634   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3635   ei->move_leave_type = getFile8Bit(file);
3636
3637   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3638   ei->move_pattern |= (getFile16BitBE(file) << 16);
3639
3640   ei->access_direction = getFile8Bit(file);
3641
3642   ei->explosion_delay = getFile8Bit(file);
3643   ei->ignition_delay = getFile8Bit(file);
3644   ei->explosion_type = getFile8Bit(file);
3645
3646   // some free bytes for future custom property values and padding
3647   ReadUnusedBytesFromFile(file, 1);
3648
3649   // ---------- change page property values (48 bytes) ------------------------
3650
3651   setElementChangePages(ei, ei->num_change_pages);
3652
3653   for (i = 0; i < ei->num_change_pages; i++)
3654   {
3655     struct ElementChangeInfo *change = &ei->change_page[i];
3656     unsigned int event_bits;
3657
3658     // always start with reliable default values
3659     setElementChangeInfoToDefaults(change);
3660
3661     // bits 0 - 31 of "has_event[]" ...
3662     event_bits = getFile32BitBE(file);
3663     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3664       if (event_bits & (1u << j))
3665         change->has_event[j] = TRUE;
3666
3667     change->target_element = getMappedElement(getFile16BitBE(file));
3668
3669     change->delay_fixed = getFile16BitBE(file);
3670     change->delay_random = getFile16BitBE(file);
3671     change->delay_frames = getFile16BitBE(file);
3672
3673     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3674
3675     change->explode = getFile8Bit(file);
3676     change->use_target_content = getFile8Bit(file);
3677     change->only_if_complete = getFile8Bit(file);
3678     change->use_random_replace = getFile8Bit(file);
3679
3680     change->random_percentage = getFile8Bit(file);
3681     change->replace_when = getFile8Bit(file);
3682
3683     for (y = 0; y < 3; y++)
3684       for (x = 0; x < 3; x++)
3685         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3686
3687     change->can_change = getFile8Bit(file);
3688
3689     change->trigger_side = getFile8Bit(file);
3690
3691     change->trigger_player = getFile8Bit(file);
3692     change->trigger_page = getFile8Bit(file);
3693
3694     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3695                             CH_PAGE_ANY : (1 << change->trigger_page));
3696
3697     change->has_action = getFile8Bit(file);
3698     change->action_type = getFile8Bit(file);
3699     change->action_mode = getFile8Bit(file);
3700     change->action_arg = getFile16BitBE(file);
3701
3702     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3703     event_bits = getFile8Bit(file);
3704     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3705       if (event_bits & (1u << (j - 32)))
3706         change->has_event[j] = TRUE;
3707   }
3708
3709   // mark this custom element as modified
3710   ei->modified_settings = TRUE;
3711
3712   level->file_has_custom_elements = TRUE;
3713
3714   return chunk_size;
3715 }
3716
3717 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3718 {
3719   struct ElementInfo *ei;
3720   struct ElementGroupInfo *group;
3721   int element;
3722   int i;
3723
3724   element = getMappedElement(getFile16BitBE(file));
3725
3726   if (!IS_GROUP_ELEMENT(element))
3727   {
3728     Warn("invalid group element number %d", element);
3729
3730     ReadUnusedBytesFromFile(file, chunk_size - 2);
3731
3732     return chunk_size;
3733   }
3734
3735   ei = &element_info[element];
3736
3737   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3738     ei->description[i] = getFile8Bit(file);
3739   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3740
3741   group = element_info[element].group;
3742
3743   group->num_elements = getFile8Bit(file);
3744
3745   ei->use_gfx_element = getFile8Bit(file);
3746   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3747
3748   group->choice_mode = getFile8Bit(file);
3749
3750   // some free bytes for future values and padding
3751   ReadUnusedBytesFromFile(file, 3);
3752
3753   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3754     group->element[i] = getMappedElement(getFile16BitBE(file));
3755
3756   // mark this group element as modified
3757   element_info[element].modified_settings = TRUE;
3758
3759   level->file_has_custom_elements = TRUE;
3760
3761   return chunk_size;
3762 }
3763
3764 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3765                                 int element, int real_element)
3766 {
3767   int micro_chunk_size = 0;
3768   int conf_type = getFile8Bit(file);
3769   int byte_mask = conf_type & CONF_MASK_BYTES;
3770   boolean element_found = FALSE;
3771   int i;
3772
3773   micro_chunk_size += 1;
3774
3775   if (byte_mask == CONF_MASK_MULTI_BYTES)
3776   {
3777     int num_bytes = getFile16BitBE(file);
3778     byte *buffer = checked_malloc(num_bytes);
3779
3780     ReadBytesFromFile(file, buffer, num_bytes);
3781
3782     for (i = 0; conf[i].data_type != -1; i++)
3783     {
3784       if (conf[i].element == element &&
3785           conf[i].conf_type == conf_type)
3786       {
3787         int data_type = conf[i].data_type;
3788         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3789         int max_num_entities = conf[i].max_num_entities;
3790
3791         if (num_entities > max_num_entities)
3792         {
3793           Warn("truncating number of entities for element %d from %d to %d",
3794                element, num_entities, max_num_entities);
3795
3796           num_entities = max_num_entities;
3797         }
3798
3799         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3800                                   data_type == TYPE_CONTENT_LIST))
3801         {
3802           // for element and content lists, zero entities are not allowed
3803           Warn("found empty list of entities for element %d", element);
3804
3805           // do not set "num_entities" here to prevent reading behind buffer
3806
3807           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3808         }
3809         else
3810         {
3811           *(int *)(conf[i].num_entities) = num_entities;
3812         }
3813
3814         element_found = TRUE;
3815
3816         if (data_type == TYPE_STRING)
3817         {
3818           char *string = (char *)(conf[i].value);
3819           int j;
3820
3821           for (j = 0; j < max_num_entities; j++)
3822             string[j] = (j < num_entities ? buffer[j] : '\0');
3823         }
3824         else if (data_type == TYPE_ELEMENT_LIST)
3825         {
3826           int *element_array = (int *)(conf[i].value);
3827           int j;
3828
3829           for (j = 0; j < num_entities; j++)
3830             element_array[j] =
3831               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3832         }
3833         else if (data_type == TYPE_CONTENT_LIST)
3834         {
3835           struct Content *content= (struct Content *)(conf[i].value);
3836           int c, x, y;
3837
3838           for (c = 0; c < num_entities; c++)
3839             for (y = 0; y < 3; y++)
3840               for (x = 0; x < 3; x++)
3841                 content[c].e[x][y] =
3842                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3843         }
3844         else
3845           element_found = FALSE;
3846
3847         break;
3848       }
3849     }
3850
3851     checked_free(buffer);
3852
3853     micro_chunk_size += 2 + num_bytes;
3854   }
3855   else          // constant size configuration data (1, 2 or 4 bytes)
3856   {
3857     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3858                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3859                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3860
3861     for (i = 0; conf[i].data_type != -1; i++)
3862     {
3863       if (conf[i].element == element &&
3864           conf[i].conf_type == conf_type)
3865       {
3866         int data_type = conf[i].data_type;
3867
3868         if (data_type == TYPE_ELEMENT)
3869           value = getMappedElement(value);
3870
3871         if (data_type == TYPE_BOOLEAN)
3872           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3873         else
3874           *(int *)    (conf[i].value) = value;
3875
3876         element_found = TRUE;
3877
3878         break;
3879       }
3880     }
3881
3882     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3883   }
3884
3885   if (!element_found)
3886   {
3887     char *error_conf_chunk_bytes =
3888       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3889        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3890        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3891     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3892     int error_element = real_element;
3893
3894     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3895          error_conf_chunk_bytes, error_conf_chunk_token,
3896          error_element, EL_NAME(error_element));
3897   }
3898
3899   return micro_chunk_size;
3900 }
3901
3902 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int real_chunk_size = 0;
3905
3906   li = *level;          // copy level data into temporary buffer
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3911
3912     if (real_chunk_size >= chunk_size)
3913       break;
3914   }
3915
3916   *level = li;          // copy temporary buffer back to level data
3917
3918   return real_chunk_size;
3919 }
3920
3921 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3922 {
3923   int real_chunk_size = 0;
3924
3925   li = *level;          // copy level data into temporary buffer
3926
3927   while (!checkEndOfFile(file))
3928   {
3929     int element = getMappedElement(getFile16BitBE(file));
3930
3931     real_chunk_size += 2;
3932     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3933                                             element, element);
3934     if (real_chunk_size >= chunk_size)
3935       break;
3936   }
3937
3938   *level = li;          // copy temporary buffer back to level data
3939
3940   return real_chunk_size;
3941 }
3942
3943 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3944 {
3945   int real_chunk_size = 0;
3946
3947   li = *level;          // copy level data into temporary buffer
3948
3949   while (!checkEndOfFile(file))
3950   {
3951     int element = getMappedElement(getFile16BitBE(file));
3952
3953     real_chunk_size += 2;
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3955                                             element, element);
3956     if (real_chunk_size >= chunk_size)
3957       break;
3958   }
3959
3960   *level = li;          // copy temporary buffer back to level data
3961
3962   return real_chunk_size;
3963 }
3964
3965 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3966 {
3967   int element = getMappedElement(getFile16BitBE(file));
3968   int envelope_nr = element - EL_ENVELOPE_1;
3969   int real_chunk_size = 2;
3970
3971   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3972
3973   while (!checkEndOfFile(file))
3974   {
3975     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3976                                             -1, element);
3977
3978     if (real_chunk_size >= chunk_size)
3979       break;
3980   }
3981
3982   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3983
3984   return real_chunk_size;
3985 }
3986
3987 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3988 {
3989   int element = getMappedElement(getFile16BitBE(file));
3990   int real_chunk_size = 2;
3991   struct ElementInfo *ei = &element_info[element];
3992   int i;
3993
3994   xx_ei = *ei;          // copy element data into temporary buffer
3995
3996   xx_ei.num_change_pages = -1;
3997
3998   while (!checkEndOfFile(file))
3999   {
4000     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
4001                                             -1, element);
4002     if (xx_ei.num_change_pages != -1)
4003       break;
4004
4005     if (real_chunk_size >= chunk_size)
4006       break;
4007   }
4008
4009   *ei = xx_ei;
4010
4011   if (ei->num_change_pages == -1)
4012   {
4013     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4014          EL_NAME(element));
4015
4016     ei->num_change_pages = 1;
4017
4018     setElementChangePages(ei, 1);
4019     setElementChangeInfoToDefaults(ei->change);
4020
4021     return real_chunk_size;
4022   }
4023
4024   // initialize number of change pages stored for this custom element
4025   setElementChangePages(ei, ei->num_change_pages);
4026   for (i = 0; i < ei->num_change_pages; i++)
4027     setElementChangeInfoToDefaults(&ei->change_page[i]);
4028
4029   // start with reading properties for the first change page
4030   xx_current_change_page = 0;
4031
4032   while (!checkEndOfFile(file))
4033   {
4034     // level file might contain invalid change page number
4035     if (xx_current_change_page >= ei->num_change_pages)
4036       break;
4037
4038     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4039
4040     xx_change = *change;        // copy change data into temporary buffer
4041
4042     resetEventBits();           // reset bits; change page might have changed
4043
4044     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4045                                             -1, element);
4046
4047     *change = xx_change;
4048
4049     setEventFlagsFromEventBits(change);
4050
4051     if (real_chunk_size >= chunk_size)
4052       break;
4053   }
4054
4055   level->file_has_custom_elements = TRUE;
4056
4057   return real_chunk_size;
4058 }
4059
4060 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4061 {
4062   int element = getMappedElement(getFile16BitBE(file));
4063   int real_chunk_size = 2;
4064   struct ElementInfo *ei = &element_info[element];
4065   struct ElementGroupInfo *group = ei->group;
4066
4067   if (group == NULL)
4068     return -1;
4069
4070   xx_ei = *ei;          // copy element data into temporary buffer
4071   xx_group = *group;    // copy group data into temporary buffer
4072
4073   while (!checkEndOfFile(file))
4074   {
4075     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4076                                             -1, element);
4077
4078     if (real_chunk_size >= chunk_size)
4079       break;
4080   }
4081
4082   *ei = xx_ei;
4083   *group = xx_group;
4084
4085   level->file_has_custom_elements = TRUE;
4086
4087   return real_chunk_size;
4088 }
4089
4090 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4091 {
4092   int element = getMappedElement(getFile16BitBE(file));
4093   int real_chunk_size = 2;
4094   struct ElementInfo *ei = &element_info[element];
4095
4096   xx_ei = *ei;          // copy element data into temporary buffer
4097
4098   while (!checkEndOfFile(file))
4099   {
4100     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4101                                             -1, element);
4102
4103     if (real_chunk_size >= chunk_size)
4104       break;
4105   }
4106
4107   *ei = xx_ei;
4108
4109   level->file_has_custom_elements = TRUE;
4110
4111   return real_chunk_size;
4112 }
4113
4114 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4115                                       struct LevelFileInfo *level_file_info,
4116                                       boolean level_info_only)
4117 {
4118   char *filename = level_file_info->filename;
4119   char cookie[MAX_LINE_LEN];
4120   char chunk_name[CHUNK_ID_LEN + 1];
4121   int chunk_size;
4122   File *file;
4123
4124   if (!(file = openFile(filename, MODE_READ)))
4125   {
4126     level->no_valid_file = TRUE;
4127     level->no_level_file = TRUE;
4128
4129     if (level_info_only)
4130       return;
4131
4132     Warn("cannot read level '%s' -- using empty level", filename);
4133
4134     if (!setup.editor.use_template_for_new_levels)
4135       return;
4136
4137     // if level file not found, try to initialize level data from template
4138     filename = getGlobalLevelTemplateFilename();
4139
4140     if (!(file = openFile(filename, MODE_READ)))
4141       return;
4142
4143     // default: for empty levels, use level template for custom elements
4144     level->use_custom_template = TRUE;
4145
4146     level->no_valid_file = FALSE;
4147   }
4148
4149   getFileChunkBE(file, chunk_name, NULL);
4150   if (strEqual(chunk_name, "RND1"))
4151   {
4152     getFile32BitBE(file);               // not used
4153
4154     getFileChunkBE(file, chunk_name, NULL);
4155     if (!strEqual(chunk_name, "CAVE"))
4156     {
4157       level->no_valid_file = TRUE;
4158
4159       Warn("unknown format of level file '%s'", filename);
4160
4161       closeFile(file);
4162
4163       return;
4164     }
4165   }
4166   else  // check for pre-2.0 file format with cookie string
4167   {
4168     strcpy(cookie, chunk_name);
4169     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4170       cookie[4] = '\0';
4171     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4172       cookie[strlen(cookie) - 1] = '\0';
4173
4174     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4175     {
4176       level->no_valid_file = TRUE;
4177
4178       Warn("unknown format of level file '%s'", filename);
4179
4180       closeFile(file);
4181
4182       return;
4183     }
4184
4185     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4186     {
4187       level->no_valid_file = TRUE;
4188
4189       Warn("unsupported version of level file '%s'", filename);
4190
4191       closeFile(file);
4192
4193       return;
4194     }
4195
4196     // pre-2.0 level files have no game version, so use file version here
4197     level->game_version = level->file_version;
4198   }
4199
4200   if (level->file_version < FILE_VERSION_1_2)
4201   {
4202     // level files from versions before 1.2.0 without chunk structure
4203     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4204     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4205   }
4206   else
4207   {
4208     static struct
4209     {
4210       char *name;
4211       int size;
4212       int (*loader)(File *, int, struct LevelInfo *);
4213     }
4214     chunk_info[] =
4215     {
4216       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4217       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4218       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4219       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4220       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4221       { "INFO", -1,                     LoadLevel_INFO },
4222       { "BODY", -1,                     LoadLevel_BODY },
4223       { "CONT", -1,                     LoadLevel_CONT },
4224       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4225       { "CNT3", -1,                     LoadLevel_CNT3 },
4226       { "CUS1", -1,                     LoadLevel_CUS1 },
4227       { "CUS2", -1,                     LoadLevel_CUS2 },
4228       { "CUS3", -1,                     LoadLevel_CUS3 },
4229       { "CUS4", -1,                     LoadLevel_CUS4 },
4230       { "GRP1", -1,                     LoadLevel_GRP1 },
4231       { "CONF", -1,                     LoadLevel_CONF },
4232       { "ELEM", -1,                     LoadLevel_ELEM },
4233       { "NOTE", -1,                     LoadLevel_NOTE },
4234       { "CUSX", -1,                     LoadLevel_CUSX },
4235       { "GRPX", -1,                     LoadLevel_GRPX },
4236       { "EMPX", -1,                     LoadLevel_EMPX },
4237
4238       {  NULL,  0,                      NULL }
4239     };
4240
4241     while (getFileChunkBE(file, chunk_name, &chunk_size))
4242     {
4243       int i = 0;
4244
4245       while (chunk_info[i].name != NULL &&
4246              !strEqual(chunk_name, chunk_info[i].name))
4247         i++;
4248
4249       if (chunk_info[i].name == NULL)
4250       {
4251         Warn("unknown chunk '%s' in level file '%s'",
4252              chunk_name, filename);
4253
4254         ReadUnusedBytesFromFile(file, chunk_size);
4255       }
4256       else if (chunk_info[i].size != -1 &&
4257                chunk_info[i].size != chunk_size)
4258       {
4259         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4260              chunk_size, chunk_name, filename);
4261
4262         ReadUnusedBytesFromFile(file, chunk_size);
4263       }
4264       else
4265       {
4266         // call function to load this level chunk
4267         int chunk_size_expected =
4268           (chunk_info[i].loader)(file, chunk_size, level);
4269
4270         if (chunk_size_expected < 0)
4271         {
4272           Warn("error reading chunk '%s' in level file '%s'",
4273                chunk_name, filename);
4274
4275           break;
4276         }
4277
4278         // the size of some chunks cannot be checked before reading other
4279         // chunks first (like "HEAD" and "BODY") that contain some header
4280         // information, so check them here
4281         if (chunk_size_expected != chunk_size)
4282         {
4283           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4284                chunk_size, chunk_name, filename);
4285
4286           break;
4287         }
4288       }
4289     }
4290   }
4291
4292   closeFile(file);
4293 }
4294
4295
4296 // ----------------------------------------------------------------------------
4297 // functions for loading BD level
4298 // ----------------------------------------------------------------------------
4299
4300 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4301 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4302
4303 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4304 {
4305   struct LevelInfo_BD *level_bd = level->native_bd_level;
4306   GdCave *cave = NULL;  // will be changed below
4307   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4308   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4309   int x, y;
4310
4311   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4312
4313   // cave and map newly allocated when set to defaults above
4314   cave = level_bd->cave;
4315
4316   // level type
4317   cave->intermission                    = level->bd_intermission;
4318
4319   // level settings
4320   cave->level_time[0]                   = level->time;
4321   cave->level_diamonds[0]               = level->gems_needed;
4322
4323   // game timing
4324   cave->scheduling                      = level->bd_scheduling_type;
4325   cave->pal_timing                      = level->bd_pal_timing;
4326   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4327   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4328   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4329   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4330
4331   // scores
4332   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4333   cave->diamond_value                   = level->score[SC_EMERALD];
4334   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4335
4336   // compatibility settings
4337   cave->lineshift                       = level->bd_line_shifting_borders;
4338   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4339   cave->short_explosions                = level->bd_short_explosions;
4340
4341   // player properties
4342   cave->diagonal_movements              = level->bd_diagonal_movements;
4343   cave->active_is_first_found           = level->bd_topmost_player_active;
4344   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4345   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4346   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4347   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4348
4349   // element properties
4350   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4351   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4352   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4353   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4354   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4355   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4356   cave->level_magic_wall_time[0]        = level->bd_magic_wall_time;
4357   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4358   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4359   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4360   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4361
4362   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4363   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4364   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4365   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4366   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4367   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4368   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4369
4370   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4371   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4372   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4373   cave->level_amoeba_threshold[0]       = level->bd_amoeba_1_threshold_too_big;
4374   cave->level_amoeba_time[0]            = level->bd_amoeba_1_slow_growth_time;
4375   cave->amoeba_growth_prob              = level->bd_amoeba_1_slow_growth_rate * 10000;
4376   cave->amoeba_fast_growth_prob         = level->bd_amoeba_1_fast_growth_rate * 10000;
4377   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4378   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4379   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4380   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4381
4382   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4383   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4384   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4385   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4386   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4387   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4388
4389   cave->slime_predictable               = level->bd_slime_is_predictable;
4390   cave->slime_correct_random            = level->bd_slime_correct_random;
4391   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4392   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4393   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4394   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4395   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4396   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4397   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4398   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4399   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4400   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4401
4402   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4403   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4404   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4405
4406   cave->biter_delay_frame               = level->bd_biter_move_delay;
4407   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4408
4409   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4410
4411   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4412
4413   cave->replicators_active              = level->bd_replicators_active;
4414   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4415
4416   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4417   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4418
4419   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4420
4421   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4422
4423   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4424   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4425   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4426
4427   cave->infinite_rockets                = level->bd_infinite_rockets;
4428
4429   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4430   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4431
4432   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4433   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4434
4435   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4436   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4437   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4438
4439   cave->gravity                         = level->bd_gravity_direction;
4440   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4441   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4442   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4443
4444   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4445   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4446   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4447   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4448
4449   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4450   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4451   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4452   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4453   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4454   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4455
4456   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4457   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4458   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4459   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4460
4461   cave->colorb                          = level->bd_color_b;
4462   cave->color0                          = level->bd_color_0;
4463   cave->color1                          = level->bd_color_1;
4464   cave->color2                          = level->bd_color_2;
4465   cave->color3                          = level->bd_color_3;
4466   cave->color4                          = level->bd_color_4;
4467   cave->color5                          = level->bd_color_5;
4468
4469   // level name
4470   strncpy(cave->name, level->name, sizeof(GdString));
4471   cave->name[sizeof(GdString) - 1] = '\0';
4472
4473   // playfield elements
4474   for (x = 0; x < cave->w; x++)
4475     for (y = 0; y < cave->h; y++)
4476       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4477 }
4478
4479 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4480 {
4481   struct LevelInfo_BD *level_bd = level->native_bd_level;
4482   GdCave *cave = level_bd->cave;
4483   int bd_level_nr = level_bd->level_nr;
4484   int x, y;
4485
4486   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4487   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4488
4489   // level type
4490   level->bd_intermission                = cave->intermission;
4491
4492   // level settings
4493   level->time                           = cave->level_time[bd_level_nr];
4494   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4495
4496   // game timing
4497   level->bd_scheduling_type             = cave->scheduling;
4498   level->bd_pal_timing                  = cave->pal_timing;
4499   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4500   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4501   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4502   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4503
4504   // scores
4505   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4506   level->score[SC_EMERALD]              = cave->diamond_value;
4507   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4508
4509   // compatibility settings
4510   level->bd_line_shifting_borders       = cave->lineshift;
4511   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4512   level->bd_short_explosions            = cave->short_explosions;
4513
4514   // player properties
4515   level->bd_diagonal_movements          = cave->diagonal_movements;
4516   level->bd_topmost_player_active       = cave->active_is_first_found;
4517   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4518   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4519   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4520   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4521
4522   // element properties
4523   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4524   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4525   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4526   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4527   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4528   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4529   level->bd_magic_wall_time             = cave->level_magic_wall_time[bd_level_nr];
4530   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4531   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4532   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4533   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4534
4535   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4536   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4537   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4538   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4539   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4540   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4541   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4542
4543   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4544   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4545   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4546   level->bd_amoeba_1_threshold_too_big  = cave->level_amoeba_threshold[bd_level_nr];
4547   level->bd_amoeba_1_slow_growth_time   = cave->level_amoeba_time[bd_level_nr];
4548   level->bd_amoeba_1_slow_growth_rate   = cave->amoeba_growth_prob      / 10000;
4549   level->bd_amoeba_1_fast_growth_rate   = cave->amoeba_fast_growth_prob / 10000;
4550   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4551   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4552   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4553   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4554
4555   level->bd_amoeba_1_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4556   level->bd_amoeba_1_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4557   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4558   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4559   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4560   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4561
4562   level->bd_slime_is_predictable        = cave->slime_predictable;
4563   level->bd_slime_correct_random        = cave->slime_correct_random;
4564   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4565   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4566   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4567   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4568   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4569   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4570   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4571   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4572   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4573   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4574
4575   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4576   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4577   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4578
4579   level->bd_biter_move_delay            = cave->biter_delay_frame;
4580   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4581
4582   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4583
4584   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4585
4586   level->bd_replicators_active          = cave->replicators_active;
4587   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4588
4589   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4590   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4591
4592   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4593
4594   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4595
4596   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4597   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4598   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4599
4600   level->bd_infinite_rockets            = cave->infinite_rockets;
4601
4602   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4603   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4604
4605   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4606   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4607
4608   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4609   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4610   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4611
4612   level->bd_gravity_direction           = cave->gravity;
4613   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4614   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4615   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4616
4617   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4618   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4619   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4620   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4621
4622   level->bd_firefly_1_explodes_to       = CAVE_TO_LEVEL(cave->firefly_explode_to);
4623   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4624   level->bd_butterfly_1_explodes_to     = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4625   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4626   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4627   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4628
4629   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4630   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4631   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4632   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4633
4634   level->bd_color_b                     = cave->colorb;
4635   level->bd_color_0                     = cave->color0;
4636   level->bd_color_1                     = cave->color1;
4637   level->bd_color_2                     = cave->color2;
4638   level->bd_color_3                     = cave->color3;
4639   level->bd_color_4                     = cave->color4;
4640   level->bd_color_5                     = cave->color5;
4641
4642   // set default color type and colors for BD style level colors
4643   SetDefaultLevelColorType_BD();
4644   SetDefaultLevelColors_BD();
4645
4646   // level name
4647   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4648
4649   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4650   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4651
4652   // playfield elements
4653   for (x = 0; x < level->fieldx; x++)
4654     for (y = 0; y < level->fieldy; y++)
4655       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4656
4657   checked_free(cave_name);
4658 }
4659
4660 static void setTapeInfoToDefaults(void);
4661
4662 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4663 {
4664   struct LevelInfo_BD *level_bd = level->native_bd_level;
4665   GdCave *cave = level_bd->cave;
4666   GdReplay *replay = level_bd->replay;
4667   int i;
4668
4669   if (replay == NULL)
4670     return;
4671
4672   // always start with reliable default values
4673   setTapeInfoToDefaults();
4674
4675   tape.level_nr = level_nr;             // (currently not used)
4676   tape.random_seed = replay->seed;
4677
4678   TapeSetDateFromIsoDateString(replay->date);
4679
4680   tape.counter = 0;
4681   tape.pos[tape.counter].delay = 0;
4682
4683   tape.bd_replay = TRUE;
4684
4685   // all time calculations only used to display approximate tape time
4686   int cave_speed = cave->speed;
4687   int milliseconds_game = 0;
4688   int milliseconds_elapsed = 20;
4689
4690   for (i = 0; i < replay->movements->len; i++)
4691   {
4692     int replay_action = replay->movements->data[i];
4693     int tape_action = map_action_BD_to_RND(replay_action);
4694     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4695     boolean success = 0;
4696
4697     while (1)
4698     {
4699       success = TapeAddAction(action);
4700
4701       milliseconds_game += milliseconds_elapsed;
4702
4703       if (milliseconds_game >= cave_speed)
4704       {
4705         milliseconds_game -= cave_speed;
4706
4707         break;
4708       }
4709     }
4710
4711     tape.counter++;
4712     tape.pos[tape.counter].delay = 0;
4713     tape.pos[tape.counter].action[0] = 0;
4714
4715     if (!success)
4716     {
4717       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4718
4719       break;
4720     }
4721   }
4722
4723   TapeHaltRecording();
4724
4725   if (!replay->success)
4726     Warn("BD replay is marked as not successful");
4727 }
4728
4729
4730 // ----------------------------------------------------------------------------
4731 // functions for loading EM level
4732 // ----------------------------------------------------------------------------
4733
4734 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4735 {
4736   static int ball_xy[8][2] =
4737   {
4738     { 0, 0 },
4739     { 1, 0 },
4740     { 2, 0 },
4741     { 0, 1 },
4742     { 2, 1 },
4743     { 0, 2 },
4744     { 1, 2 },
4745     { 2, 2 },
4746   };
4747   struct LevelInfo_EM *level_em = level->native_em_level;
4748   struct CAVE *cav = level_em->cav;
4749   int i, j, x, y;
4750
4751   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4752   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4753
4754   cav->time_seconds     = level->time;
4755   cav->gems_needed      = level->gems_needed;
4756
4757   cav->emerald_score    = level->score[SC_EMERALD];
4758   cav->diamond_score    = level->score[SC_DIAMOND];
4759   cav->alien_score      = level->score[SC_ROBOT];
4760   cav->tank_score       = level->score[SC_SPACESHIP];
4761   cav->bug_score        = level->score[SC_BUG];
4762   cav->eater_score      = level->score[SC_YAMYAM];
4763   cav->nut_score        = level->score[SC_NUT];
4764   cav->dynamite_score   = level->score[SC_DYNAMITE];
4765   cav->key_score        = level->score[SC_KEY];
4766   cav->exit_score       = level->score[SC_TIME_BONUS];
4767
4768   cav->num_eater_arrays = level->num_yamyam_contents;
4769
4770   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4771     for (y = 0; y < 3; y++)
4772       for (x = 0; x < 3; x++)
4773         cav->eater_array[i][y * 3 + x] =
4774           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4775
4776   cav->amoeba_time              = level->amoeba_speed;
4777   cav->wonderwall_time          = level->time_magic_wall;
4778   cav->wheel_time               = level->time_wheel;
4779
4780   cav->android_move_time        = level->android_move_time;
4781   cav->android_clone_time       = level->android_clone_time;
4782   cav->ball_random              = level->ball_random;
4783   cav->ball_active              = level->ball_active_initial;
4784   cav->ball_time                = level->ball_time;
4785   cav->num_ball_arrays          = level->num_ball_contents;
4786
4787   cav->lenses_score             = level->lenses_score;
4788   cav->magnify_score            = level->magnify_score;
4789   cav->slurp_score              = level->slurp_score;
4790
4791   cav->lenses_time              = level->lenses_time;
4792   cav->magnify_time             = level->magnify_time;
4793
4794   cav->wind_time = 9999;
4795   cav->wind_direction =
4796     map_direction_RND_to_EM(level->wind_direction_initial);
4797
4798   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4799     for (j = 0; j < 8; j++)
4800       cav->ball_array[i][j] =
4801         map_element_RND_to_EM_cave(level->ball_content[i].
4802                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4803
4804   map_android_clone_elements_RND_to_EM(level);
4805
4806   // first fill the complete playfield with the empty space element
4807   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4808     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4809       cav->cave[x][y] = Cblank;
4810
4811   // then copy the real level contents from level file into the playfield
4812   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4813   {
4814     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4815
4816     if (level->field[x][y] == EL_AMOEBA_DEAD)
4817       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4818
4819     cav->cave[x][y] = new_element;
4820   }
4821
4822   for (i = 0; i < MAX_PLAYERS; i++)
4823   {
4824     cav->player_x[i] = -1;
4825     cav->player_y[i] = -1;
4826   }
4827
4828   // initialize player positions and delete players from the playfield
4829   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4830   {
4831     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4832     {
4833       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4834
4835       cav->player_x[player_nr] = x;
4836       cav->player_y[player_nr] = y;
4837
4838       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4839     }
4840   }
4841 }
4842
4843 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4844 {
4845   static int ball_xy[8][2] =
4846   {
4847     { 0, 0 },
4848     { 1, 0 },
4849     { 2, 0 },
4850     { 0, 1 },
4851     { 2, 1 },
4852     { 0, 2 },
4853     { 1, 2 },
4854     { 2, 2 },
4855   };
4856   struct LevelInfo_EM *level_em = level->native_em_level;
4857   struct CAVE *cav = level_em->cav;
4858   int i, j, x, y;
4859
4860   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4861   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4862
4863   level->time        = cav->time_seconds;
4864   level->gems_needed = cav->gems_needed;
4865
4866   sprintf(level->name, "Level %d", level->file_info.nr);
4867
4868   level->score[SC_EMERALD]      = cav->emerald_score;
4869   level->score[SC_DIAMOND]      = cav->diamond_score;
4870   level->score[SC_ROBOT]        = cav->alien_score;
4871   level->score[SC_SPACESHIP]    = cav->tank_score;
4872   level->score[SC_BUG]          = cav->bug_score;
4873   level->score[SC_YAMYAM]       = cav->eater_score;
4874   level->score[SC_NUT]          = cav->nut_score;
4875   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4876   level->score[SC_KEY]          = cav->key_score;
4877   level->score[SC_TIME_BONUS]   = cav->exit_score;
4878
4879   level->num_yamyam_contents    = cav->num_eater_arrays;
4880
4881   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4882     for (y = 0; y < 3; y++)
4883       for (x = 0; x < 3; x++)
4884         level->yamyam_content[i].e[x][y] =
4885           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4886
4887   level->amoeba_speed           = cav->amoeba_time;
4888   level->time_magic_wall        = cav->wonderwall_time;
4889   level->time_wheel             = cav->wheel_time;
4890
4891   level->android_move_time      = cav->android_move_time;
4892   level->android_clone_time     = cav->android_clone_time;
4893   level->ball_random            = cav->ball_random;
4894   level->ball_active_initial    = cav->ball_active;
4895   level->ball_time              = cav->ball_time;
4896   level->num_ball_contents      = cav->num_ball_arrays;
4897
4898   level->lenses_score           = cav->lenses_score;
4899   level->magnify_score          = cav->magnify_score;
4900   level->slurp_score            = cav->slurp_score;
4901
4902   level->lenses_time            = cav->lenses_time;
4903   level->magnify_time           = cav->magnify_time;
4904
4905   level->wind_direction_initial =
4906     map_direction_EM_to_RND(cav->wind_direction);
4907
4908   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4909     for (j = 0; j < 8; j++)
4910       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4911         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4912
4913   map_android_clone_elements_EM_to_RND(level);
4914
4915   // convert the playfield (some elements need special treatment)
4916   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4917   {
4918     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4919
4920     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4921       new_element = EL_AMOEBA_DEAD;
4922
4923     level->field[x][y] = new_element;
4924   }
4925
4926   for (i = 0; i < MAX_PLAYERS; i++)
4927   {
4928     // in case of all players set to the same field, use the first player
4929     int nr = MAX_PLAYERS - i - 1;
4930     int jx = cav->player_x[nr];
4931     int jy = cav->player_y[nr];
4932
4933     if (jx != -1 && jy != -1)
4934       level->field[jx][jy] = EL_PLAYER_1 + nr;
4935   }
4936
4937   // time score is counted for each 10 seconds left in Emerald Mine levels
4938   level->time_score_base = 10;
4939 }
4940
4941
4942 // ----------------------------------------------------------------------------
4943 // functions for loading SP level
4944 // ----------------------------------------------------------------------------
4945
4946 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4947 {
4948   struct LevelInfo_SP *level_sp = level->native_sp_level;
4949   LevelInfoType *header = &level_sp->header;
4950   int i, x, y;
4951
4952   level_sp->width  = level->fieldx;
4953   level_sp->height = level->fieldy;
4954
4955   for (x = 0; x < level->fieldx; x++)
4956     for (y = 0; y < level->fieldy; y++)
4957       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4958
4959   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4960
4961   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4962     header->LevelTitle[i] = level->name[i];
4963   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4964
4965   header->InfotronsNeeded = level->gems_needed;
4966
4967   header->SpecialPortCount = 0;
4968
4969   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4970   {
4971     boolean gravity_port_found = FALSE;
4972     boolean gravity_port_valid = FALSE;
4973     int gravity_port_flag;
4974     int gravity_port_base_element;
4975     int element = level->field[x][y];
4976
4977     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4978         element <= EL_SP_GRAVITY_ON_PORT_UP)
4979     {
4980       gravity_port_found = TRUE;
4981       gravity_port_valid = TRUE;
4982       gravity_port_flag = 1;
4983       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4984     }
4985     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4986              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4987     {
4988       gravity_port_found = TRUE;
4989       gravity_port_valid = TRUE;
4990       gravity_port_flag = 0;
4991       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4992     }
4993     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4994              element <= EL_SP_GRAVITY_PORT_UP)
4995     {
4996       // change R'n'D style gravity inverting special port to normal port
4997       // (there are no gravity inverting ports in native Supaplex engine)
4998
4999       gravity_port_found = TRUE;
5000       gravity_port_valid = FALSE;
5001       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
5002     }
5003
5004     if (gravity_port_found)
5005     {
5006       if (gravity_port_valid &&
5007           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5008       {
5009         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5010
5011         port->PortLocation = (y * level->fieldx + x) * 2;
5012         port->Gravity = gravity_port_flag;
5013
5014         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5015
5016         header->SpecialPortCount++;
5017       }
5018       else
5019       {
5020         // change special gravity port to normal port
5021
5022         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5023       }
5024
5025       level_sp->playfield[x][y] = element - EL_SP_START;
5026     }
5027   }
5028 }
5029
5030 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5031 {
5032   struct LevelInfo_SP *level_sp = level->native_sp_level;
5033   LevelInfoType *header = &level_sp->header;
5034   boolean num_invalid_elements = 0;
5035   int i, j, x, y;
5036
5037   level->fieldx = level_sp->width;
5038   level->fieldy = level_sp->height;
5039
5040   for (x = 0; x < level->fieldx; x++)
5041   {
5042     for (y = 0; y < level->fieldy; y++)
5043     {
5044       int element_old = level_sp->playfield[x][y];
5045       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5046
5047       if (element_new == EL_UNKNOWN)
5048       {
5049         num_invalid_elements++;
5050
5051         Debug("level:native:SP", "invalid element %d at position %d, %d",
5052               element_old, x, y);
5053       }
5054
5055       level->field[x][y] = element_new;
5056     }
5057   }
5058
5059   if (num_invalid_elements > 0)
5060     Warn("found %d invalid elements%s", num_invalid_elements,
5061          (!options.debug ? " (use '--debug' for more details)" : ""));
5062
5063   for (i = 0; i < MAX_PLAYERS; i++)
5064     level->initial_player_gravity[i] =
5065       (header->InitialGravity == 1 ? TRUE : FALSE);
5066
5067   // skip leading spaces
5068   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5069     if (header->LevelTitle[i] != ' ')
5070       break;
5071
5072   // copy level title
5073   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5074     level->name[j] = header->LevelTitle[i];
5075   level->name[j] = '\0';
5076
5077   // cut trailing spaces
5078   for (; j > 0; j--)
5079     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5080       level->name[j - 1] = '\0';
5081
5082   level->gems_needed = header->InfotronsNeeded;
5083
5084   for (i = 0; i < header->SpecialPortCount; i++)
5085   {
5086     SpecialPortType *port = &header->SpecialPort[i];
5087     int port_location = port->PortLocation;
5088     int gravity = port->Gravity;
5089     int port_x, port_y, port_element;
5090
5091     port_x = (port_location / 2) % level->fieldx;
5092     port_y = (port_location / 2) / level->fieldx;
5093
5094     if (port_x < 0 || port_x >= level->fieldx ||
5095         port_y < 0 || port_y >= level->fieldy)
5096     {
5097       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5098
5099       continue;
5100     }
5101
5102     port_element = level->field[port_x][port_y];
5103
5104     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5105         port_element > EL_SP_GRAVITY_PORT_UP)
5106     {
5107       Warn("no special port at position (%d, %d)", port_x, port_y);
5108
5109       continue;
5110     }
5111
5112     // change previous (wrong) gravity inverting special port to either
5113     // gravity enabling special port or gravity disabling special port
5114     level->field[port_x][port_y] +=
5115       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5116        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5117   }
5118
5119   // change special gravity ports without database entries to normal ports
5120   for (x = 0; x < level->fieldx; x++)
5121     for (y = 0; y < level->fieldy; y++)
5122       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5123           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5124         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5125
5126   level->time = 0;                      // no time limit
5127   level->amoeba_speed = 0;
5128   level->time_magic_wall = 0;
5129   level->time_wheel = 0;
5130   level->amoeba_content = EL_EMPTY;
5131
5132   // original Supaplex does not use score values -- rate by playing time
5133   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5134     level->score[i] = 0;
5135
5136   level->rate_time_over_score = TRUE;
5137
5138   // there are no yamyams in supaplex levels
5139   for (i = 0; i < level->num_yamyam_contents; i++)
5140     for (x = 0; x < 3; x++)
5141       for (y = 0; y < 3; y++)
5142         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5143 }
5144
5145 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5146 {
5147   struct LevelInfo_SP *level_sp = level->native_sp_level;
5148   struct DemoInfo_SP *demo = &level_sp->demo;
5149   int i, j;
5150
5151   // always start with reliable default values
5152   demo->is_available = FALSE;
5153   demo->length = 0;
5154
5155   if (TAPE_IS_EMPTY(tape))
5156     return;
5157
5158   demo->level_nr = tape.level_nr;       // (currently not used)
5159
5160   level_sp->header.DemoRandomSeed = tape.random_seed;
5161
5162   demo->length = 0;
5163
5164   for (i = 0; i < tape.length; i++)
5165   {
5166     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5167     int demo_repeat = tape.pos[i].delay;
5168     int demo_entries = (demo_repeat + 15) / 16;
5169
5170     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5171     {
5172       Warn("tape truncated: size exceeds maximum SP demo size %d",
5173            SP_MAX_TAPE_LEN);
5174
5175       break;
5176     }
5177
5178     for (j = 0; j < demo_repeat / 16; j++)
5179       demo->data[demo->length++] = 0xf0 | demo_action;
5180
5181     if (demo_repeat % 16)
5182       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5183   }
5184
5185   demo->is_available = TRUE;
5186 }
5187
5188 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5189 {
5190   struct LevelInfo_SP *level_sp = level->native_sp_level;
5191   struct DemoInfo_SP *demo = &level_sp->demo;
5192   char *filename = level->file_info.filename;
5193   int i;
5194
5195   // always start with reliable default values
5196   setTapeInfoToDefaults();
5197
5198   if (!demo->is_available)
5199     return;
5200
5201   tape.level_nr = demo->level_nr;       // (currently not used)
5202   tape.random_seed = level_sp->header.DemoRandomSeed;
5203
5204   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5205
5206   tape.counter = 0;
5207   tape.pos[tape.counter].delay = 0;
5208
5209   for (i = 0; i < demo->length; i++)
5210   {
5211     int demo_action = demo->data[i] & 0x0f;
5212     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5213     int tape_action = map_key_SP_to_RND(demo_action);
5214     int tape_repeat = demo_repeat + 1;
5215     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5216     boolean success = 0;
5217     int j;
5218
5219     for (j = 0; j < tape_repeat; j++)
5220       success = TapeAddAction(action);
5221
5222     if (!success)
5223     {
5224       Warn("SP demo truncated: size exceeds maximum tape size %d",
5225            MAX_TAPE_LEN);
5226
5227       break;
5228     }
5229   }
5230
5231   TapeHaltRecording();
5232 }
5233
5234
5235 // ----------------------------------------------------------------------------
5236 // functions for loading MM level
5237 // ----------------------------------------------------------------------------
5238
5239 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5240 {
5241   struct LevelInfo_MM *level_mm = level->native_mm_level;
5242   int i, x, y;
5243
5244   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5245   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5246
5247   level_mm->time = level->time;
5248   level_mm->kettles_needed = level->gems_needed;
5249   level_mm->auto_count_kettles = level->auto_count_gems;
5250
5251   level_mm->mm_laser_red   = level->mm_laser_red;
5252   level_mm->mm_laser_green = level->mm_laser_green;
5253   level_mm->mm_laser_blue  = level->mm_laser_blue;
5254
5255   level_mm->df_laser_red   = level->df_laser_red;
5256   level_mm->df_laser_green = level->df_laser_green;
5257   level_mm->df_laser_blue  = level->df_laser_blue;
5258
5259   strcpy(level_mm->name, level->name);
5260   strcpy(level_mm->author, level->author);
5261
5262   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5263   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5264   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5265   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5266   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5267
5268   level_mm->amoeba_speed = level->amoeba_speed;
5269   level_mm->time_fuse    = level->mm_time_fuse;
5270   level_mm->time_bomb    = level->mm_time_bomb;
5271   level_mm->time_ball    = level->mm_time_ball;
5272   level_mm->time_block   = level->mm_time_block;
5273
5274   level_mm->num_ball_contents = level->num_mm_ball_contents;
5275   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5276   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5277   level_mm->explode_ball = level->explode_mm_ball;
5278
5279   for (i = 0; i < level->num_mm_ball_contents; i++)
5280     level_mm->ball_content[i] =
5281       map_element_RND_to_MM(level->mm_ball_content[i]);
5282
5283   for (x = 0; x < level->fieldx; x++)
5284     for (y = 0; y < level->fieldy; y++)
5285       Ur[x][y] =
5286         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5287 }
5288
5289 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5290 {
5291   struct LevelInfo_MM *level_mm = level->native_mm_level;
5292   int i, x, y;
5293
5294   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5295   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5296
5297   level->time = level_mm->time;
5298   level->gems_needed = level_mm->kettles_needed;
5299   level->auto_count_gems = level_mm->auto_count_kettles;
5300
5301   level->mm_laser_red   = level_mm->mm_laser_red;
5302   level->mm_laser_green = level_mm->mm_laser_green;
5303   level->mm_laser_blue  = level_mm->mm_laser_blue;
5304
5305   level->df_laser_red   = level_mm->df_laser_red;
5306   level->df_laser_green = level_mm->df_laser_green;
5307   level->df_laser_blue  = level_mm->df_laser_blue;
5308
5309   strcpy(level->name, level_mm->name);
5310
5311   // only overwrite author from 'levelinfo.conf' if author defined in level
5312   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5313     strcpy(level->author, level_mm->author);
5314
5315   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5316   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5317   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5318   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5319   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5320
5321   level->amoeba_speed  = level_mm->amoeba_speed;
5322   level->mm_time_fuse  = level_mm->time_fuse;
5323   level->mm_time_bomb  = level_mm->time_bomb;
5324   level->mm_time_ball  = level_mm->time_ball;
5325   level->mm_time_block = level_mm->time_block;
5326
5327   level->num_mm_ball_contents = level_mm->num_ball_contents;
5328   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5329   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5330   level->explode_mm_ball = level_mm->explode_ball;
5331
5332   for (i = 0; i < level->num_mm_ball_contents; i++)
5333     level->mm_ball_content[i] =
5334       map_element_MM_to_RND(level_mm->ball_content[i]);
5335
5336   for (x = 0; x < level->fieldx; x++)
5337     for (y = 0; y < level->fieldy; y++)
5338       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5339 }
5340
5341
5342 // ----------------------------------------------------------------------------
5343 // functions for loading DC level
5344 // ----------------------------------------------------------------------------
5345
5346 #define DC_LEVEL_HEADER_SIZE            344
5347
5348 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5349                                         boolean init)
5350 {
5351   static int last_data_encoded;
5352   static int offset1;
5353   static int offset2;
5354   int diff;
5355   int diff_hi, diff_lo;
5356   int data_hi, data_lo;
5357   unsigned short data_decoded;
5358
5359   if (init)
5360   {
5361     last_data_encoded = 0;
5362     offset1 = -1;
5363     offset2 = 0;
5364
5365     return 0;
5366   }
5367
5368   diff = data_encoded - last_data_encoded;
5369   diff_hi = diff & ~0xff;
5370   diff_lo = diff &  0xff;
5371
5372   offset2 += diff_lo;
5373
5374   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5375   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5376   data_hi = data_hi & 0xff00;
5377
5378   data_decoded = data_hi | data_lo;
5379
5380   last_data_encoded = data_encoded;
5381
5382   offset1 = (offset1 + 1) % 31;
5383   offset2 = offset2 & 0xff;
5384
5385   return data_decoded;
5386 }
5387
5388 static int getMappedElement_DC(int element)
5389 {
5390   switch (element)
5391   {
5392     case 0x0000:
5393       element = EL_ROCK;
5394       break;
5395
5396       // 0x0117 - 0x036e: (?)
5397       // EL_DIAMOND
5398
5399       // 0x042d - 0x0684: (?)
5400       // EL_EMERALD
5401
5402     case 0x06f1:
5403       element = EL_NUT;
5404       break;
5405
5406     case 0x074c:
5407       element = EL_BOMB;
5408       break;
5409
5410     case 0x07a4:
5411       element = EL_PEARL;
5412       break;
5413
5414     case 0x0823:
5415       element = EL_CRYSTAL;
5416       break;
5417
5418     case 0x0e77:        // quicksand (boulder)
5419       element = EL_QUICKSAND_FAST_FULL;
5420       break;
5421
5422     case 0x0e99:        // slow quicksand (boulder)
5423       element = EL_QUICKSAND_FULL;
5424       break;
5425
5426     case 0x0ed2:
5427       element = EL_EM_EXIT_OPEN;
5428       break;
5429
5430     case 0x0ee3:
5431       element = EL_EM_EXIT_CLOSED;
5432       break;
5433
5434     case 0x0eeb:
5435       element = EL_EM_STEEL_EXIT_OPEN;
5436       break;
5437
5438     case 0x0efc:
5439       element = EL_EM_STEEL_EXIT_CLOSED;
5440       break;
5441
5442     case 0x0f4f:        // dynamite (lit 1)
5443       element = EL_EM_DYNAMITE_ACTIVE;
5444       break;
5445
5446     case 0x0f57:        // dynamite (lit 2)
5447       element = EL_EM_DYNAMITE_ACTIVE;
5448       break;
5449
5450     case 0x0f5f:        // dynamite (lit 3)
5451       element = EL_EM_DYNAMITE_ACTIVE;
5452       break;
5453
5454     case 0x0f67:        // dynamite (lit 4)
5455       element = EL_EM_DYNAMITE_ACTIVE;
5456       break;
5457
5458     case 0x0f81:
5459     case 0x0f82:
5460     case 0x0f83:
5461     case 0x0f84:
5462       element = EL_AMOEBA_WET;
5463       break;
5464
5465     case 0x0f85:
5466       element = EL_AMOEBA_DROP;
5467       break;
5468
5469     case 0x0fb9:
5470       element = EL_DC_MAGIC_WALL;
5471       break;
5472
5473     case 0x0fd0:
5474       element = EL_SPACESHIP_UP;
5475       break;
5476
5477     case 0x0fd9:
5478       element = EL_SPACESHIP_DOWN;
5479       break;
5480
5481     case 0x0ff1:
5482       element = EL_SPACESHIP_LEFT;
5483       break;
5484
5485     case 0x0ff9:
5486       element = EL_SPACESHIP_RIGHT;
5487       break;
5488
5489     case 0x1057:
5490       element = EL_BUG_UP;
5491       break;
5492
5493     case 0x1060:
5494       element = EL_BUG_DOWN;
5495       break;
5496
5497     case 0x1078:
5498       element = EL_BUG_LEFT;
5499       break;
5500
5501     case 0x1080:
5502       element = EL_BUG_RIGHT;
5503       break;
5504
5505     case 0x10de:
5506       element = EL_MOLE_UP;
5507       break;
5508
5509     case 0x10e7:
5510       element = EL_MOLE_DOWN;
5511       break;
5512
5513     case 0x10ff:
5514       element = EL_MOLE_LEFT;
5515       break;
5516
5517     case 0x1107:
5518       element = EL_MOLE_RIGHT;
5519       break;
5520
5521     case 0x11c0:
5522       element = EL_ROBOT;
5523       break;
5524
5525     case 0x13f5:
5526       element = EL_YAMYAM_UP;
5527       break;
5528
5529     case 0x1425:
5530       element = EL_SWITCHGATE_OPEN;
5531       break;
5532
5533     case 0x1426:
5534       element = EL_SWITCHGATE_CLOSED;
5535       break;
5536
5537     case 0x1437:
5538       element = EL_DC_SWITCHGATE_SWITCH_UP;
5539       break;
5540
5541     case 0x143a:
5542       element = EL_TIMEGATE_CLOSED;
5543       break;
5544
5545     case 0x144c:        // conveyor belt switch (green)
5546       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5547       break;
5548
5549     case 0x144f:        // conveyor belt switch (red)
5550       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5551       break;
5552
5553     case 0x1452:        // conveyor belt switch (blue)
5554       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5555       break;
5556
5557     case 0x145b:
5558       element = EL_CONVEYOR_BELT_3_MIDDLE;
5559       break;
5560
5561     case 0x1463:
5562       element = EL_CONVEYOR_BELT_3_LEFT;
5563       break;
5564
5565     case 0x146b:
5566       element = EL_CONVEYOR_BELT_3_RIGHT;
5567       break;
5568
5569     case 0x1473:
5570       element = EL_CONVEYOR_BELT_1_MIDDLE;
5571       break;
5572
5573     case 0x147b:
5574       element = EL_CONVEYOR_BELT_1_LEFT;
5575       break;
5576
5577     case 0x1483:
5578       element = EL_CONVEYOR_BELT_1_RIGHT;
5579       break;
5580
5581     case 0x148b:
5582       element = EL_CONVEYOR_BELT_4_MIDDLE;
5583       break;
5584
5585     case 0x1493:
5586       element = EL_CONVEYOR_BELT_4_LEFT;
5587       break;
5588
5589     case 0x149b:
5590       element = EL_CONVEYOR_BELT_4_RIGHT;
5591       break;
5592
5593     case 0x14ac:
5594       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5595       break;
5596
5597     case 0x14bd:
5598       element = EL_EXPANDABLE_WALL_VERTICAL;
5599       break;
5600
5601     case 0x14c6:
5602       element = EL_EXPANDABLE_WALL_ANY;
5603       break;
5604
5605     case 0x14ce:        // growing steel wall (left/right)
5606       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5607       break;
5608
5609     case 0x14df:        // growing steel wall (up/down)
5610       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5611       break;
5612
5613     case 0x14e8:        // growing steel wall (up/down/left/right)
5614       element = EL_EXPANDABLE_STEELWALL_ANY;
5615       break;
5616
5617     case 0x14e9:
5618       element = EL_SHIELD_DEADLY;
5619       break;
5620
5621     case 0x1501:
5622       element = EL_EXTRA_TIME;
5623       break;
5624
5625     case 0x154f:
5626       element = EL_ACID;
5627       break;
5628
5629     case 0x1577:
5630       element = EL_EMPTY_SPACE;
5631       break;
5632
5633     case 0x1578:        // quicksand (empty)
5634       element = EL_QUICKSAND_FAST_EMPTY;
5635       break;
5636
5637     case 0x1579:        // slow quicksand (empty)
5638       element = EL_QUICKSAND_EMPTY;
5639       break;
5640
5641       // 0x157c - 0x158b:
5642       // EL_SAND
5643
5644       // 0x1590 - 0x159f:
5645       // EL_DC_LANDMINE
5646
5647     case 0x15a0:
5648       element = EL_EM_DYNAMITE;
5649       break;
5650
5651     case 0x15a1:        // key (red)
5652       element = EL_EM_KEY_1;
5653       break;
5654
5655     case 0x15a2:        // key (yellow)
5656       element = EL_EM_KEY_2;
5657       break;
5658
5659     case 0x15a3:        // key (blue)
5660       element = EL_EM_KEY_4;
5661       break;
5662
5663     case 0x15a4:        // key (green)
5664       element = EL_EM_KEY_3;
5665       break;
5666
5667     case 0x15a5:        // key (white)
5668       element = EL_DC_KEY_WHITE;
5669       break;
5670
5671     case 0x15a6:
5672       element = EL_WALL_SLIPPERY;
5673       break;
5674
5675     case 0x15a7:
5676       element = EL_WALL;
5677       break;
5678
5679     case 0x15a8:        // wall (not round)
5680       element = EL_WALL;
5681       break;
5682
5683     case 0x15a9:        // (blue)
5684       element = EL_CHAR_A;
5685       break;
5686
5687     case 0x15aa:        // (blue)
5688       element = EL_CHAR_B;
5689       break;
5690
5691     case 0x15ab:        // (blue)
5692       element = EL_CHAR_C;
5693       break;
5694
5695     case 0x15ac:        // (blue)
5696       element = EL_CHAR_D;
5697       break;
5698
5699     case 0x15ad:        // (blue)
5700       element = EL_CHAR_E;
5701       break;
5702
5703     case 0x15ae:        // (blue)
5704       element = EL_CHAR_F;
5705       break;
5706
5707     case 0x15af:        // (blue)
5708       element = EL_CHAR_G;
5709       break;
5710
5711     case 0x15b0:        // (blue)
5712       element = EL_CHAR_H;
5713       break;
5714
5715     case 0x15b1:        // (blue)
5716       element = EL_CHAR_I;
5717       break;
5718
5719     case 0x15b2:        // (blue)
5720       element = EL_CHAR_J;
5721       break;
5722
5723     case 0x15b3:        // (blue)
5724       element = EL_CHAR_K;
5725       break;
5726
5727     case 0x15b4:        // (blue)
5728       element = EL_CHAR_L;
5729       break;
5730
5731     case 0x15b5:        // (blue)
5732       element = EL_CHAR_M;
5733       break;
5734
5735     case 0x15b6:        // (blue)
5736       element = EL_CHAR_N;
5737       break;
5738
5739     case 0x15b7:        // (blue)
5740       element = EL_CHAR_O;
5741       break;
5742
5743     case 0x15b8:        // (blue)
5744       element = EL_CHAR_P;
5745       break;
5746
5747     case 0x15b9:        // (blue)
5748       element = EL_CHAR_Q;
5749       break;
5750
5751     case 0x15ba:        // (blue)
5752       element = EL_CHAR_R;
5753       break;
5754
5755     case 0x15bb:        // (blue)
5756       element = EL_CHAR_S;
5757       break;
5758
5759     case 0x15bc:        // (blue)
5760       element = EL_CHAR_T;
5761       break;
5762
5763     case 0x15bd:        // (blue)
5764       element = EL_CHAR_U;
5765       break;
5766
5767     case 0x15be:        // (blue)
5768       element = EL_CHAR_V;
5769       break;
5770
5771     case 0x15bf:        // (blue)
5772       element = EL_CHAR_W;
5773       break;
5774
5775     case 0x15c0:        // (blue)
5776       element = EL_CHAR_X;
5777       break;
5778
5779     case 0x15c1:        // (blue)
5780       element = EL_CHAR_Y;
5781       break;
5782
5783     case 0x15c2:        // (blue)
5784       element = EL_CHAR_Z;
5785       break;
5786
5787     case 0x15c3:        // (blue)
5788       element = EL_CHAR_AUMLAUT;
5789       break;
5790
5791     case 0x15c4:        // (blue)
5792       element = EL_CHAR_OUMLAUT;
5793       break;
5794
5795     case 0x15c5:        // (blue)
5796       element = EL_CHAR_UUMLAUT;
5797       break;
5798
5799     case 0x15c6:        // (blue)
5800       element = EL_CHAR_0;
5801       break;
5802
5803     case 0x15c7:        // (blue)
5804       element = EL_CHAR_1;
5805       break;
5806
5807     case 0x15c8:        // (blue)
5808       element = EL_CHAR_2;
5809       break;
5810
5811     case 0x15c9:        // (blue)
5812       element = EL_CHAR_3;
5813       break;
5814
5815     case 0x15ca:        // (blue)
5816       element = EL_CHAR_4;
5817       break;
5818
5819     case 0x15cb:        // (blue)
5820       element = EL_CHAR_5;
5821       break;
5822
5823     case 0x15cc:        // (blue)
5824       element = EL_CHAR_6;
5825       break;
5826
5827     case 0x15cd:        // (blue)
5828       element = EL_CHAR_7;
5829       break;
5830
5831     case 0x15ce:        // (blue)
5832       element = EL_CHAR_8;
5833       break;
5834
5835     case 0x15cf:        // (blue)
5836       element = EL_CHAR_9;
5837       break;
5838
5839     case 0x15d0:        // (blue)
5840       element = EL_CHAR_PERIOD;
5841       break;
5842
5843     case 0x15d1:        // (blue)
5844       element = EL_CHAR_EXCLAM;
5845       break;
5846
5847     case 0x15d2:        // (blue)
5848       element = EL_CHAR_COLON;
5849       break;
5850
5851     case 0x15d3:        // (blue)
5852       element = EL_CHAR_LESS;
5853       break;
5854
5855     case 0x15d4:        // (blue)
5856       element = EL_CHAR_GREATER;
5857       break;
5858
5859     case 0x15d5:        // (blue)
5860       element = EL_CHAR_QUESTION;
5861       break;
5862
5863     case 0x15d6:        // (blue)
5864       element = EL_CHAR_COPYRIGHT;
5865       break;
5866
5867     case 0x15d7:        // (blue)
5868       element = EL_CHAR_UP;
5869       break;
5870
5871     case 0x15d8:        // (blue)
5872       element = EL_CHAR_DOWN;
5873       break;
5874
5875     case 0x15d9:        // (blue)
5876       element = EL_CHAR_BUTTON;
5877       break;
5878
5879     case 0x15da:        // (blue)
5880       element = EL_CHAR_PLUS;
5881       break;
5882
5883     case 0x15db:        // (blue)
5884       element = EL_CHAR_MINUS;
5885       break;
5886
5887     case 0x15dc:        // (blue)
5888       element = EL_CHAR_APOSTROPHE;
5889       break;
5890
5891     case 0x15dd:        // (blue)
5892       element = EL_CHAR_PARENLEFT;
5893       break;
5894
5895     case 0x15de:        // (blue)
5896       element = EL_CHAR_PARENRIGHT;
5897       break;
5898
5899     case 0x15df:        // (green)
5900       element = EL_CHAR_A;
5901       break;
5902
5903     case 0x15e0:        // (green)
5904       element = EL_CHAR_B;
5905       break;
5906
5907     case 0x15e1:        // (green)
5908       element = EL_CHAR_C;
5909       break;
5910
5911     case 0x15e2:        // (green)
5912       element = EL_CHAR_D;
5913       break;
5914
5915     case 0x15e3:        // (green)
5916       element = EL_CHAR_E;
5917       break;
5918
5919     case 0x15e4:        // (green)
5920       element = EL_CHAR_F;
5921       break;
5922
5923     case 0x15e5:        // (green)
5924       element = EL_CHAR_G;
5925       break;
5926
5927     case 0x15e6:        // (green)
5928       element = EL_CHAR_H;
5929       break;
5930
5931     case 0x15e7:        // (green)
5932       element = EL_CHAR_I;
5933       break;
5934
5935     case 0x15e8:        // (green)
5936       element = EL_CHAR_J;
5937       break;
5938
5939     case 0x15e9:        // (green)
5940       element = EL_CHAR_K;
5941       break;
5942
5943     case 0x15ea:        // (green)
5944       element = EL_CHAR_L;
5945       break;
5946
5947     case 0x15eb:        // (green)
5948       element = EL_CHAR_M;
5949       break;
5950
5951     case 0x15ec:        // (green)
5952       element = EL_CHAR_N;
5953       break;
5954
5955     case 0x15ed:        // (green)
5956       element = EL_CHAR_O;
5957       break;
5958
5959     case 0x15ee:        // (green)
5960       element = EL_CHAR_P;
5961       break;
5962
5963     case 0x15ef:        // (green)
5964       element = EL_CHAR_Q;
5965       break;
5966
5967     case 0x15f0:        // (green)
5968       element = EL_CHAR_R;
5969       break;
5970
5971     case 0x15f1:        // (green)
5972       element = EL_CHAR_S;
5973       break;
5974
5975     case 0x15f2:        // (green)
5976       element = EL_CHAR_T;
5977       break;
5978
5979     case 0x15f3:        // (green)
5980       element = EL_CHAR_U;
5981       break;
5982
5983     case 0x15f4:        // (green)
5984       element = EL_CHAR_V;
5985       break;
5986
5987     case 0x15f5:        // (green)
5988       element = EL_CHAR_W;
5989       break;
5990
5991     case 0x15f6:        // (green)
5992       element = EL_CHAR_X;
5993       break;
5994
5995     case 0x15f7:        // (green)
5996       element = EL_CHAR_Y;
5997       break;
5998
5999     case 0x15f8:        // (green)
6000       element = EL_CHAR_Z;
6001       break;
6002
6003     case 0x15f9:        // (green)
6004       element = EL_CHAR_AUMLAUT;
6005       break;
6006
6007     case 0x15fa:        // (green)
6008       element = EL_CHAR_OUMLAUT;
6009       break;
6010
6011     case 0x15fb:        // (green)
6012       element = EL_CHAR_UUMLAUT;
6013       break;
6014
6015     case 0x15fc:        // (green)
6016       element = EL_CHAR_0;
6017       break;
6018
6019     case 0x15fd:        // (green)
6020       element = EL_CHAR_1;
6021       break;
6022
6023     case 0x15fe:        // (green)
6024       element = EL_CHAR_2;
6025       break;
6026
6027     case 0x15ff:        // (green)
6028       element = EL_CHAR_3;
6029       break;
6030
6031     case 0x1600:        // (green)
6032       element = EL_CHAR_4;
6033       break;
6034
6035     case 0x1601:        // (green)
6036       element = EL_CHAR_5;
6037       break;
6038
6039     case 0x1602:        // (green)
6040       element = EL_CHAR_6;
6041       break;
6042
6043     case 0x1603:        // (green)
6044       element = EL_CHAR_7;
6045       break;
6046
6047     case 0x1604:        // (green)
6048       element = EL_CHAR_8;
6049       break;
6050
6051     case 0x1605:        // (green)
6052       element = EL_CHAR_9;
6053       break;
6054
6055     case 0x1606:        // (green)
6056       element = EL_CHAR_PERIOD;
6057       break;
6058
6059     case 0x1607:        // (green)
6060       element = EL_CHAR_EXCLAM;
6061       break;
6062
6063     case 0x1608:        // (green)
6064       element = EL_CHAR_COLON;
6065       break;
6066
6067     case 0x1609:        // (green)
6068       element = EL_CHAR_LESS;
6069       break;
6070
6071     case 0x160a:        // (green)
6072       element = EL_CHAR_GREATER;
6073       break;
6074
6075     case 0x160b:        // (green)
6076       element = EL_CHAR_QUESTION;
6077       break;
6078
6079     case 0x160c:        // (green)
6080       element = EL_CHAR_COPYRIGHT;
6081       break;
6082
6083     case 0x160d:        // (green)
6084       element = EL_CHAR_UP;
6085       break;
6086
6087     case 0x160e:        // (green)
6088       element = EL_CHAR_DOWN;
6089       break;
6090
6091     case 0x160f:        // (green)
6092       element = EL_CHAR_BUTTON;
6093       break;
6094
6095     case 0x1610:        // (green)
6096       element = EL_CHAR_PLUS;
6097       break;
6098
6099     case 0x1611:        // (green)
6100       element = EL_CHAR_MINUS;
6101       break;
6102
6103     case 0x1612:        // (green)
6104       element = EL_CHAR_APOSTROPHE;
6105       break;
6106
6107     case 0x1613:        // (green)
6108       element = EL_CHAR_PARENLEFT;
6109       break;
6110
6111     case 0x1614:        // (green)
6112       element = EL_CHAR_PARENRIGHT;
6113       break;
6114
6115     case 0x1615:        // (blue steel)
6116       element = EL_STEEL_CHAR_A;
6117       break;
6118
6119     case 0x1616:        // (blue steel)
6120       element = EL_STEEL_CHAR_B;
6121       break;
6122
6123     case 0x1617:        // (blue steel)
6124       element = EL_STEEL_CHAR_C;
6125       break;
6126
6127     case 0x1618:        // (blue steel)
6128       element = EL_STEEL_CHAR_D;
6129       break;
6130
6131     case 0x1619:        // (blue steel)
6132       element = EL_STEEL_CHAR_E;
6133       break;
6134
6135     case 0x161a:        // (blue steel)
6136       element = EL_STEEL_CHAR_F;
6137       break;
6138
6139     case 0x161b:        // (blue steel)
6140       element = EL_STEEL_CHAR_G;
6141       break;
6142
6143     case 0x161c:        // (blue steel)
6144       element = EL_STEEL_CHAR_H;
6145       break;
6146
6147     case 0x161d:        // (blue steel)
6148       element = EL_STEEL_CHAR_I;
6149       break;
6150
6151     case 0x161e:        // (blue steel)
6152       element = EL_STEEL_CHAR_J;
6153       break;
6154
6155     case 0x161f:        // (blue steel)
6156       element = EL_STEEL_CHAR_K;
6157       break;
6158
6159     case 0x1620:        // (blue steel)
6160       element = EL_STEEL_CHAR_L;
6161       break;
6162
6163     case 0x1621:        // (blue steel)
6164       element = EL_STEEL_CHAR_M;
6165       break;
6166
6167     case 0x1622:        // (blue steel)
6168       element = EL_STEEL_CHAR_N;
6169       break;
6170
6171     case 0x1623:        // (blue steel)
6172       element = EL_STEEL_CHAR_O;
6173       break;
6174
6175     case 0x1624:        // (blue steel)
6176       element = EL_STEEL_CHAR_P;
6177       break;
6178
6179     case 0x1625:        // (blue steel)
6180       element = EL_STEEL_CHAR_Q;
6181       break;
6182
6183     case 0x1626:        // (blue steel)
6184       element = EL_STEEL_CHAR_R;
6185       break;
6186
6187     case 0x1627:        // (blue steel)
6188       element = EL_STEEL_CHAR_S;
6189       break;
6190
6191     case 0x1628:        // (blue steel)
6192       element = EL_STEEL_CHAR_T;
6193       break;
6194
6195     case 0x1629:        // (blue steel)
6196       element = EL_STEEL_CHAR_U;
6197       break;
6198
6199     case 0x162a:        // (blue steel)
6200       element = EL_STEEL_CHAR_V;
6201       break;
6202
6203     case 0x162b:        // (blue steel)
6204       element = EL_STEEL_CHAR_W;
6205       break;
6206
6207     case 0x162c:        // (blue steel)
6208       element = EL_STEEL_CHAR_X;
6209       break;
6210
6211     case 0x162d:        // (blue steel)
6212       element = EL_STEEL_CHAR_Y;
6213       break;
6214
6215     case 0x162e:        // (blue steel)
6216       element = EL_STEEL_CHAR_Z;
6217       break;
6218
6219     case 0x162f:        // (blue steel)
6220       element = EL_STEEL_CHAR_AUMLAUT;
6221       break;
6222
6223     case 0x1630:        // (blue steel)
6224       element = EL_STEEL_CHAR_OUMLAUT;
6225       break;
6226
6227     case 0x1631:        // (blue steel)
6228       element = EL_STEEL_CHAR_UUMLAUT;
6229       break;
6230
6231     case 0x1632:        // (blue steel)
6232       element = EL_STEEL_CHAR_0;
6233       break;
6234
6235     case 0x1633:        // (blue steel)
6236       element = EL_STEEL_CHAR_1;
6237       break;
6238
6239     case 0x1634:        // (blue steel)
6240       element = EL_STEEL_CHAR_2;
6241       break;
6242
6243     case 0x1635:        // (blue steel)
6244       element = EL_STEEL_CHAR_3;
6245       break;
6246
6247     case 0x1636:        // (blue steel)
6248       element = EL_STEEL_CHAR_4;
6249       break;
6250
6251     case 0x1637:        // (blue steel)
6252       element = EL_STEEL_CHAR_5;
6253       break;
6254
6255     case 0x1638:        // (blue steel)
6256       element = EL_STEEL_CHAR_6;
6257       break;
6258
6259     case 0x1639:        // (blue steel)
6260       element = EL_STEEL_CHAR_7;
6261       break;
6262
6263     case 0x163a:        // (blue steel)
6264       element = EL_STEEL_CHAR_8;
6265       break;
6266
6267     case 0x163b:        // (blue steel)
6268       element = EL_STEEL_CHAR_9;
6269       break;
6270
6271     case 0x163c:        // (blue steel)
6272       element = EL_STEEL_CHAR_PERIOD;
6273       break;
6274
6275     case 0x163d:        // (blue steel)
6276       element = EL_STEEL_CHAR_EXCLAM;
6277       break;
6278
6279     case 0x163e:        // (blue steel)
6280       element = EL_STEEL_CHAR_COLON;
6281       break;
6282
6283     case 0x163f:        // (blue steel)
6284       element = EL_STEEL_CHAR_LESS;
6285       break;
6286
6287     case 0x1640:        // (blue steel)
6288       element = EL_STEEL_CHAR_GREATER;
6289       break;
6290
6291     case 0x1641:        // (blue steel)
6292       element = EL_STEEL_CHAR_QUESTION;
6293       break;
6294
6295     case 0x1642:        // (blue steel)
6296       element = EL_STEEL_CHAR_COPYRIGHT;
6297       break;
6298
6299     case 0x1643:        // (blue steel)
6300       element = EL_STEEL_CHAR_UP;
6301       break;
6302
6303     case 0x1644:        // (blue steel)
6304       element = EL_STEEL_CHAR_DOWN;
6305       break;
6306
6307     case 0x1645:        // (blue steel)
6308       element = EL_STEEL_CHAR_BUTTON;
6309       break;
6310
6311     case 0x1646:        // (blue steel)
6312       element = EL_STEEL_CHAR_PLUS;
6313       break;
6314
6315     case 0x1647:        // (blue steel)
6316       element = EL_STEEL_CHAR_MINUS;
6317       break;
6318
6319     case 0x1648:        // (blue steel)
6320       element = EL_STEEL_CHAR_APOSTROPHE;
6321       break;
6322
6323     case 0x1649:        // (blue steel)
6324       element = EL_STEEL_CHAR_PARENLEFT;
6325       break;
6326
6327     case 0x164a:        // (blue steel)
6328       element = EL_STEEL_CHAR_PARENRIGHT;
6329       break;
6330
6331     case 0x164b:        // (green steel)
6332       element = EL_STEEL_CHAR_A;
6333       break;
6334
6335     case 0x164c:        // (green steel)
6336       element = EL_STEEL_CHAR_B;
6337       break;
6338
6339     case 0x164d:        // (green steel)
6340       element = EL_STEEL_CHAR_C;
6341       break;
6342
6343     case 0x164e:        // (green steel)
6344       element = EL_STEEL_CHAR_D;
6345       break;
6346
6347     case 0x164f:        // (green steel)
6348       element = EL_STEEL_CHAR_E;
6349       break;
6350
6351     case 0x1650:        // (green steel)
6352       element = EL_STEEL_CHAR_F;
6353       break;
6354
6355     case 0x1651:        // (green steel)
6356       element = EL_STEEL_CHAR_G;
6357       break;
6358
6359     case 0x1652:        // (green steel)
6360       element = EL_STEEL_CHAR_H;
6361       break;
6362
6363     case 0x1653:        // (green steel)
6364       element = EL_STEEL_CHAR_I;
6365       break;
6366
6367     case 0x1654:        // (green steel)
6368       element = EL_STEEL_CHAR_J;
6369       break;
6370
6371     case 0x1655:        // (green steel)
6372       element = EL_STEEL_CHAR_K;
6373       break;
6374
6375     case 0x1656:        // (green steel)
6376       element = EL_STEEL_CHAR_L;
6377       break;
6378
6379     case 0x1657:        // (green steel)
6380       element = EL_STEEL_CHAR_M;
6381       break;
6382
6383     case 0x1658:        // (green steel)
6384       element = EL_STEEL_CHAR_N;
6385       break;
6386
6387     case 0x1659:        // (green steel)
6388       element = EL_STEEL_CHAR_O;
6389       break;
6390
6391     case 0x165a:        // (green steel)
6392       element = EL_STEEL_CHAR_P;
6393       break;
6394
6395     case 0x165b:        // (green steel)
6396       element = EL_STEEL_CHAR_Q;
6397       break;
6398
6399     case 0x165c:        // (green steel)
6400       element = EL_STEEL_CHAR_R;
6401       break;
6402
6403     case 0x165d:        // (green steel)
6404       element = EL_STEEL_CHAR_S;
6405       break;
6406
6407     case 0x165e:        // (green steel)
6408       element = EL_STEEL_CHAR_T;
6409       break;
6410
6411     case 0x165f:        // (green steel)
6412       element = EL_STEEL_CHAR_U;
6413       break;
6414
6415     case 0x1660:        // (green steel)
6416       element = EL_STEEL_CHAR_V;
6417       break;
6418
6419     case 0x1661:        // (green steel)
6420       element = EL_STEEL_CHAR_W;
6421       break;
6422
6423     case 0x1662:        // (green steel)
6424       element = EL_STEEL_CHAR_X;
6425       break;
6426
6427     case 0x1663:        // (green steel)
6428       element = EL_STEEL_CHAR_Y;
6429       break;
6430
6431     case 0x1664:        // (green steel)
6432       element = EL_STEEL_CHAR_Z;
6433       break;
6434
6435     case 0x1665:        // (green steel)
6436       element = EL_STEEL_CHAR_AUMLAUT;
6437       break;
6438
6439     case 0x1666:        // (green steel)
6440       element = EL_STEEL_CHAR_OUMLAUT;
6441       break;
6442
6443     case 0x1667:        // (green steel)
6444       element = EL_STEEL_CHAR_UUMLAUT;
6445       break;
6446
6447     case 0x1668:        // (green steel)
6448       element = EL_STEEL_CHAR_0;
6449       break;
6450
6451     case 0x1669:        // (green steel)
6452       element = EL_STEEL_CHAR_1;
6453       break;
6454
6455     case 0x166a:        // (green steel)
6456       element = EL_STEEL_CHAR_2;
6457       break;
6458
6459     case 0x166b:        // (green steel)
6460       element = EL_STEEL_CHAR_3;
6461       break;
6462
6463     case 0x166c:        // (green steel)
6464       element = EL_STEEL_CHAR_4;
6465       break;
6466
6467     case 0x166d:        // (green steel)
6468       element = EL_STEEL_CHAR_5;
6469       break;
6470
6471     case 0x166e:        // (green steel)
6472       element = EL_STEEL_CHAR_6;
6473       break;
6474
6475     case 0x166f:        // (green steel)
6476       element = EL_STEEL_CHAR_7;
6477       break;
6478
6479     case 0x1670:        // (green steel)
6480       element = EL_STEEL_CHAR_8;
6481       break;
6482
6483     case 0x1671:        // (green steel)
6484       element = EL_STEEL_CHAR_9;
6485       break;
6486
6487     case 0x1672:        // (green steel)
6488       element = EL_STEEL_CHAR_PERIOD;
6489       break;
6490
6491     case 0x1673:        // (green steel)
6492       element = EL_STEEL_CHAR_EXCLAM;
6493       break;
6494
6495     case 0x1674:        // (green steel)
6496       element = EL_STEEL_CHAR_COLON;
6497       break;
6498
6499     case 0x1675:        // (green steel)
6500       element = EL_STEEL_CHAR_LESS;
6501       break;
6502
6503     case 0x1676:        // (green steel)
6504       element = EL_STEEL_CHAR_GREATER;
6505       break;
6506
6507     case 0x1677:        // (green steel)
6508       element = EL_STEEL_CHAR_QUESTION;
6509       break;
6510
6511     case 0x1678:        // (green steel)
6512       element = EL_STEEL_CHAR_COPYRIGHT;
6513       break;
6514
6515     case 0x1679:        // (green steel)
6516       element = EL_STEEL_CHAR_UP;
6517       break;
6518
6519     case 0x167a:        // (green steel)
6520       element = EL_STEEL_CHAR_DOWN;
6521       break;
6522
6523     case 0x167b:        // (green steel)
6524       element = EL_STEEL_CHAR_BUTTON;
6525       break;
6526
6527     case 0x167c:        // (green steel)
6528       element = EL_STEEL_CHAR_PLUS;
6529       break;
6530
6531     case 0x167d:        // (green steel)
6532       element = EL_STEEL_CHAR_MINUS;
6533       break;
6534
6535     case 0x167e:        // (green steel)
6536       element = EL_STEEL_CHAR_APOSTROPHE;
6537       break;
6538
6539     case 0x167f:        // (green steel)
6540       element = EL_STEEL_CHAR_PARENLEFT;
6541       break;
6542
6543     case 0x1680:        // (green steel)
6544       element = EL_STEEL_CHAR_PARENRIGHT;
6545       break;
6546
6547     case 0x1681:        // gate (red)
6548       element = EL_EM_GATE_1;
6549       break;
6550
6551     case 0x1682:        // secret gate (red)
6552       element = EL_EM_GATE_1_GRAY;
6553       break;
6554
6555     case 0x1683:        // gate (yellow)
6556       element = EL_EM_GATE_2;
6557       break;
6558
6559     case 0x1684:        // secret gate (yellow)
6560       element = EL_EM_GATE_2_GRAY;
6561       break;
6562
6563     case 0x1685:        // gate (blue)
6564       element = EL_EM_GATE_4;
6565       break;
6566
6567     case 0x1686:        // secret gate (blue)
6568       element = EL_EM_GATE_4_GRAY;
6569       break;
6570
6571     case 0x1687:        // gate (green)
6572       element = EL_EM_GATE_3;
6573       break;
6574
6575     case 0x1688:        // secret gate (green)
6576       element = EL_EM_GATE_3_GRAY;
6577       break;
6578
6579     case 0x1689:        // gate (white)
6580       element = EL_DC_GATE_WHITE;
6581       break;
6582
6583     case 0x168a:        // secret gate (white)
6584       element = EL_DC_GATE_WHITE_GRAY;
6585       break;
6586
6587     case 0x168b:        // secret gate (no key)
6588       element = EL_DC_GATE_FAKE_GRAY;
6589       break;
6590
6591     case 0x168c:
6592       element = EL_ROBOT_WHEEL;
6593       break;
6594
6595     case 0x168d:
6596       element = EL_DC_TIMEGATE_SWITCH;
6597       break;
6598
6599     case 0x168e:
6600       element = EL_ACID_POOL_BOTTOM;
6601       break;
6602
6603     case 0x168f:
6604       element = EL_ACID_POOL_TOPLEFT;
6605       break;
6606
6607     case 0x1690:
6608       element = EL_ACID_POOL_TOPRIGHT;
6609       break;
6610
6611     case 0x1691:
6612       element = EL_ACID_POOL_BOTTOMLEFT;
6613       break;
6614
6615     case 0x1692:
6616       element = EL_ACID_POOL_BOTTOMRIGHT;
6617       break;
6618
6619     case 0x1693:
6620       element = EL_STEELWALL;
6621       break;
6622
6623     case 0x1694:
6624       element = EL_STEELWALL_SLIPPERY;
6625       break;
6626
6627     case 0x1695:        // steel wall (not round)
6628       element = EL_STEELWALL;
6629       break;
6630
6631     case 0x1696:        // steel wall (left)
6632       element = EL_DC_STEELWALL_1_LEFT;
6633       break;
6634
6635     case 0x1697:        // steel wall (bottom)
6636       element = EL_DC_STEELWALL_1_BOTTOM;
6637       break;
6638
6639     case 0x1698:        // steel wall (right)
6640       element = EL_DC_STEELWALL_1_RIGHT;
6641       break;
6642
6643     case 0x1699:        // steel wall (top)
6644       element = EL_DC_STEELWALL_1_TOP;
6645       break;
6646
6647     case 0x169a:        // steel wall (left/bottom)
6648       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6649       break;
6650
6651     case 0x169b:        // steel wall (right/bottom)
6652       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6653       break;
6654
6655     case 0x169c:        // steel wall (right/top)
6656       element = EL_DC_STEELWALL_1_TOPRIGHT;
6657       break;
6658
6659     case 0x169d:        // steel wall (left/top)
6660       element = EL_DC_STEELWALL_1_TOPLEFT;
6661       break;
6662
6663     case 0x169e:        // steel wall (right/bottom small)
6664       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6665       break;
6666
6667     case 0x169f:        // steel wall (left/bottom small)
6668       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6669       break;
6670
6671     case 0x16a0:        // steel wall (right/top small)
6672       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6673       break;
6674
6675     case 0x16a1:        // steel wall (left/top small)
6676       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6677       break;
6678
6679     case 0x16a2:        // steel wall (left/right)
6680       element = EL_DC_STEELWALL_1_VERTICAL;
6681       break;
6682
6683     case 0x16a3:        // steel wall (top/bottom)
6684       element = EL_DC_STEELWALL_1_HORIZONTAL;
6685       break;
6686
6687     case 0x16a4:        // steel wall 2 (left end)
6688       element = EL_DC_STEELWALL_2_LEFT;
6689       break;
6690
6691     case 0x16a5:        // steel wall 2 (right end)
6692       element = EL_DC_STEELWALL_2_RIGHT;
6693       break;
6694
6695     case 0x16a6:        // steel wall 2 (top end)
6696       element = EL_DC_STEELWALL_2_TOP;
6697       break;
6698
6699     case 0x16a7:        // steel wall 2 (bottom end)
6700       element = EL_DC_STEELWALL_2_BOTTOM;
6701       break;
6702
6703     case 0x16a8:        // steel wall 2 (left/right)
6704       element = EL_DC_STEELWALL_2_HORIZONTAL;
6705       break;
6706
6707     case 0x16a9:        // steel wall 2 (up/down)
6708       element = EL_DC_STEELWALL_2_VERTICAL;
6709       break;
6710
6711     case 0x16aa:        // steel wall 2 (mid)
6712       element = EL_DC_STEELWALL_2_MIDDLE;
6713       break;
6714
6715     case 0x16ab:
6716       element = EL_SIGN_EXCLAMATION;
6717       break;
6718
6719     case 0x16ac:
6720       element = EL_SIGN_RADIOACTIVITY;
6721       break;
6722
6723     case 0x16ad:
6724       element = EL_SIGN_STOP;
6725       break;
6726
6727     case 0x16ae:
6728       element = EL_SIGN_WHEELCHAIR;
6729       break;
6730
6731     case 0x16af:
6732       element = EL_SIGN_PARKING;
6733       break;
6734
6735     case 0x16b0:
6736       element = EL_SIGN_NO_ENTRY;
6737       break;
6738
6739     case 0x16b1:
6740       element = EL_SIGN_HEART;
6741       break;
6742
6743     case 0x16b2:
6744       element = EL_SIGN_GIVE_WAY;
6745       break;
6746
6747     case 0x16b3:
6748       element = EL_SIGN_ENTRY_FORBIDDEN;
6749       break;
6750
6751     case 0x16b4:
6752       element = EL_SIGN_EMERGENCY_EXIT;
6753       break;
6754
6755     case 0x16b5:
6756       element = EL_SIGN_YIN_YANG;
6757       break;
6758
6759     case 0x16b6:
6760       element = EL_WALL_EMERALD;
6761       break;
6762
6763     case 0x16b7:
6764       element = EL_WALL_DIAMOND;
6765       break;
6766
6767     case 0x16b8:
6768       element = EL_WALL_PEARL;
6769       break;
6770
6771     case 0x16b9:
6772       element = EL_WALL_CRYSTAL;
6773       break;
6774
6775     case 0x16ba:
6776       element = EL_INVISIBLE_WALL;
6777       break;
6778
6779     case 0x16bb:
6780       element = EL_INVISIBLE_STEELWALL;
6781       break;
6782
6783       // 0x16bc - 0x16cb:
6784       // EL_INVISIBLE_SAND
6785
6786     case 0x16cc:
6787       element = EL_LIGHT_SWITCH;
6788       break;
6789
6790     case 0x16cd:
6791       element = EL_ENVELOPE_1;
6792       break;
6793
6794     default:
6795       if (element >= 0x0117 && element <= 0x036e)       // (?)
6796         element = EL_DIAMOND;
6797       else if (element >= 0x042d && element <= 0x0684)  // (?)
6798         element = EL_EMERALD;
6799       else if (element >= 0x157c && element <= 0x158b)
6800         element = EL_SAND;
6801       else if (element >= 0x1590 && element <= 0x159f)
6802         element = EL_DC_LANDMINE;
6803       else if (element >= 0x16bc && element <= 0x16cb)
6804         element = EL_INVISIBLE_SAND;
6805       else
6806       {
6807         Warn("unknown Diamond Caves element 0x%04x", element);
6808
6809         element = EL_UNKNOWN;
6810       }
6811       break;
6812   }
6813
6814   return getMappedElement(element);
6815 }
6816
6817 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6818 {
6819   byte header[DC_LEVEL_HEADER_SIZE];
6820   int envelope_size;
6821   int envelope_header_pos = 62;
6822   int envelope_content_pos = 94;
6823   int level_name_pos = 251;
6824   int level_author_pos = 292;
6825   int envelope_header_len;
6826   int envelope_content_len;
6827   int level_name_len;
6828   int level_author_len;
6829   int fieldx, fieldy;
6830   int num_yamyam_contents;
6831   int i, x, y;
6832
6833   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6834
6835   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6836   {
6837     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6838
6839     header[i * 2 + 0] = header_word >> 8;
6840     header[i * 2 + 1] = header_word & 0xff;
6841   }
6842
6843   // read some values from level header to check level decoding integrity
6844   fieldx = header[6] | (header[7] << 8);
6845   fieldy = header[8] | (header[9] << 8);
6846   num_yamyam_contents = header[60] | (header[61] << 8);
6847
6848   // do some simple sanity checks to ensure that level was correctly decoded
6849   if (fieldx < 1 || fieldx > 256 ||
6850       fieldy < 1 || fieldy > 256 ||
6851       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6852   {
6853     level->no_valid_file = TRUE;
6854
6855     Warn("cannot decode level from stream -- using empty level");
6856
6857     return;
6858   }
6859
6860   // maximum envelope header size is 31 bytes
6861   envelope_header_len   = header[envelope_header_pos];
6862   // maximum envelope content size is 110 (156?) bytes
6863   envelope_content_len  = header[envelope_content_pos];
6864
6865   // maximum level title size is 40 bytes
6866   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6867   // maximum level author size is 30 (51?) bytes
6868   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6869
6870   envelope_size = 0;
6871
6872   for (i = 0; i < envelope_header_len; i++)
6873     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6874       level->envelope[0].text[envelope_size++] =
6875         header[envelope_header_pos + 1 + i];
6876
6877   if (envelope_header_len > 0 && envelope_content_len > 0)
6878   {
6879     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6880       level->envelope[0].text[envelope_size++] = '\n';
6881     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6882       level->envelope[0].text[envelope_size++] = '\n';
6883   }
6884
6885   for (i = 0; i < envelope_content_len; i++)
6886     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6887       level->envelope[0].text[envelope_size++] =
6888         header[envelope_content_pos + 1 + i];
6889
6890   level->envelope[0].text[envelope_size] = '\0';
6891
6892   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6893   level->envelope[0].ysize = 10;
6894   level->envelope[0].autowrap = TRUE;
6895   level->envelope[0].centered = TRUE;
6896
6897   for (i = 0; i < level_name_len; i++)
6898     level->name[i] = header[level_name_pos + 1 + i];
6899   level->name[level_name_len] = '\0';
6900
6901   for (i = 0; i < level_author_len; i++)
6902     level->author[i] = header[level_author_pos + 1 + i];
6903   level->author[level_author_len] = '\0';
6904
6905   num_yamyam_contents = header[60] | (header[61] << 8);
6906   level->num_yamyam_contents =
6907     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6908
6909   for (i = 0; i < num_yamyam_contents; i++)
6910   {
6911     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6912     {
6913       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6914       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6915
6916       if (i < MAX_ELEMENT_CONTENTS)
6917         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6918     }
6919   }
6920
6921   fieldx = header[6] | (header[7] << 8);
6922   fieldy = header[8] | (header[9] << 8);
6923   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6924   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6925
6926   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6927   {
6928     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6929     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6930
6931     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6932       level->field[x][y] = getMappedElement_DC(element_dc);
6933   }
6934
6935   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6936   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6937   level->field[x][y] = EL_PLAYER_1;
6938
6939   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6940   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6941   level->field[x][y] = EL_PLAYER_2;
6942
6943   level->gems_needed            = header[18] | (header[19] << 8);
6944
6945   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6946   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6947   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6948   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6949   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6950   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6951   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6952   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6953   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6954   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6955   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6956   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6957
6958   level->time                   = header[44] | (header[45] << 8);
6959
6960   level->amoeba_speed           = header[46] | (header[47] << 8);
6961   level->time_light             = header[48] | (header[49] << 8);
6962   level->time_timegate          = header[50] | (header[51] << 8);
6963   level->time_wheel             = header[52] | (header[53] << 8);
6964   level->time_magic_wall        = header[54] | (header[55] << 8);
6965   level->extra_time             = header[56] | (header[57] << 8);
6966   level->shield_normal_time     = header[58] | (header[59] << 8);
6967
6968   // shield and extra time elements do not have a score
6969   level->score[SC_SHIELD]       = 0;
6970   level->extra_time_score       = 0;
6971
6972   // set time for normal and deadly shields to the same value
6973   level->shield_deadly_time     = level->shield_normal_time;
6974
6975   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6976   // can slip down from flat walls, like normal walls and steel walls
6977   level->em_slippery_gems = TRUE;
6978
6979   // time score is counted for each 10 seconds left in Diamond Caves levels
6980   level->time_score_base = 10;
6981 }
6982
6983 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6984                                      struct LevelFileInfo *level_file_info,
6985                                      boolean level_info_only)
6986 {
6987   char *filename = level_file_info->filename;
6988   File *file;
6989   int num_magic_bytes = 8;
6990   char magic_bytes[num_magic_bytes + 1];
6991   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6992
6993   if (!(file = openFile(filename, MODE_READ)))
6994   {
6995     level->no_valid_file = TRUE;
6996
6997     if (!level_info_only)
6998       Warn("cannot read level '%s' -- using empty level", filename);
6999
7000     return;
7001   }
7002
7003   // fseek(file, 0x0000, SEEK_SET);
7004
7005   if (level_file_info->packed)
7006   {
7007     // read "magic bytes" from start of file
7008     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7009       magic_bytes[0] = '\0';
7010
7011     // check "magic bytes" for correct file format
7012     if (!strPrefix(magic_bytes, "DC2"))
7013     {
7014       level->no_valid_file = TRUE;
7015
7016       Warn("unknown DC level file '%s' -- using empty level", filename);
7017
7018       return;
7019     }
7020
7021     if (strPrefix(magic_bytes, "DC2Win95") ||
7022         strPrefix(magic_bytes, "DC2Win98"))
7023     {
7024       int position_first_level = 0x00fa;
7025       int extra_bytes = 4;
7026       int skip_bytes;
7027
7028       // advance file stream to first level inside the level package
7029       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7030
7031       // each block of level data is followed by block of non-level data
7032       num_levels_to_skip *= 2;
7033
7034       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7035       while (num_levels_to_skip >= 0)
7036       {
7037         // advance file stream to next level inside the level package
7038         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7039         {
7040           level->no_valid_file = TRUE;
7041
7042           Warn("cannot fseek in file '%s' -- using empty level", filename);
7043
7044           return;
7045         }
7046
7047         // skip apparently unused extra bytes following each level
7048         ReadUnusedBytesFromFile(file, extra_bytes);
7049
7050         // read size of next level in level package
7051         skip_bytes = getFile32BitLE(file);
7052
7053         num_levels_to_skip--;
7054       }
7055     }
7056     else
7057     {
7058       level->no_valid_file = TRUE;
7059
7060       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7061
7062       return;
7063     }
7064   }
7065
7066   LoadLevelFromFileStream_DC(file, level);
7067
7068   closeFile(file);
7069 }
7070
7071
7072 // ----------------------------------------------------------------------------
7073 // functions for loading SB level
7074 // ----------------------------------------------------------------------------
7075
7076 int getMappedElement_SB(int element_ascii, boolean use_ces)
7077 {
7078   static struct
7079   {
7080     int ascii;
7081     int sb;
7082     int ce;
7083   }
7084   sb_element_mapping[] =
7085   {
7086     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7087     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7088     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7089     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7090     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7091     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7092     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7093     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7094
7095     { 0,   -1,                      -1          },
7096   };
7097
7098   int i;
7099
7100   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7101     if (element_ascii == sb_element_mapping[i].ascii)
7102       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7103
7104   return EL_UNDEFINED;
7105 }
7106
7107 static void SetLevelSettings_SB(struct LevelInfo *level)
7108 {
7109   // time settings
7110   level->time = 0;
7111   level->use_step_counter = TRUE;
7112
7113   // score settings
7114   level->score[SC_TIME_BONUS] = 0;
7115   level->time_score_base = 1;
7116   level->rate_time_over_score = TRUE;
7117
7118   // game settings
7119   level->auto_exit_sokoban = TRUE;
7120 }
7121
7122 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7123                                      struct LevelFileInfo *level_file_info,
7124                                      boolean level_info_only)
7125 {
7126   char *filename = level_file_info->filename;
7127   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7128   char last_comment[MAX_LINE_LEN];
7129   char level_name[MAX_LINE_LEN];
7130   char *line_ptr;
7131   File *file;
7132   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7133   boolean read_continued_line = FALSE;
7134   boolean reading_playfield = FALSE;
7135   boolean got_valid_playfield_line = FALSE;
7136   boolean invalid_playfield_char = FALSE;
7137   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7138   int file_level_nr = 0;
7139   int x = 0, y = 0;             // initialized to make compilers happy
7140
7141   last_comment[0] = '\0';
7142   level_name[0] = '\0';
7143
7144   if (!(file = openFile(filename, MODE_READ)))
7145   {
7146     level->no_valid_file = TRUE;
7147
7148     if (!level_info_only)
7149       Warn("cannot read level '%s' -- using empty level", filename);
7150
7151     return;
7152   }
7153
7154   while (!checkEndOfFile(file))
7155   {
7156     // level successfully read, but next level may follow here
7157     if (!got_valid_playfield_line && reading_playfield)
7158     {
7159       // read playfield from single level file -- skip remaining file
7160       if (!level_file_info->packed)
7161         break;
7162
7163       if (file_level_nr >= num_levels_to_skip)
7164         break;
7165
7166       file_level_nr++;
7167
7168       last_comment[0] = '\0';
7169       level_name[0] = '\0';
7170
7171       reading_playfield = FALSE;
7172     }
7173
7174     got_valid_playfield_line = FALSE;
7175
7176     // read next line of input file
7177     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7178       break;
7179
7180     // cut trailing line break (this can be newline and/or carriage return)
7181     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7182       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7183         *line_ptr = '\0';
7184
7185     // copy raw input line for later use (mainly debugging output)
7186     strcpy(line_raw, line);
7187
7188     if (read_continued_line)
7189     {
7190       // append new line to existing line, if there is enough space
7191       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7192         strcat(previous_line, line_ptr);
7193
7194       strcpy(line, previous_line);      // copy storage buffer to line
7195
7196       read_continued_line = FALSE;
7197     }
7198
7199     // if the last character is '\', continue at next line
7200     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7201     {
7202       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7203       strcpy(previous_line, line);      // copy line to storage buffer
7204
7205       read_continued_line = TRUE;
7206
7207       continue;
7208     }
7209
7210     // skip empty lines
7211     if (line[0] == '\0')
7212       continue;
7213
7214     // extract comment text from comment line
7215     if (line[0] == ';')
7216     {
7217       for (line_ptr = line; *line_ptr; line_ptr++)
7218         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7219           break;
7220
7221       strcpy(last_comment, line_ptr);
7222
7223       continue;
7224     }
7225
7226     // extract level title text from line containing level title
7227     if (line[0] == '\'')
7228     {
7229       strcpy(level_name, &line[1]);
7230
7231       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7232         level_name[strlen(level_name) - 1] = '\0';
7233
7234       continue;
7235     }
7236
7237     // skip lines containing only spaces (or empty lines)
7238     for (line_ptr = line; *line_ptr; line_ptr++)
7239       if (*line_ptr != ' ')
7240         break;
7241     if (*line_ptr == '\0')
7242       continue;
7243
7244     // at this point, we have found a line containing part of a playfield
7245
7246     got_valid_playfield_line = TRUE;
7247
7248     if (!reading_playfield)
7249     {
7250       reading_playfield = TRUE;
7251       invalid_playfield_char = FALSE;
7252
7253       for (x = 0; x < MAX_LEV_FIELDX; x++)
7254         for (y = 0; y < MAX_LEV_FIELDY; y++)
7255           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7256
7257       level->fieldx = 0;
7258       level->fieldy = 0;
7259
7260       // start with topmost tile row
7261       y = 0;
7262     }
7263
7264     // skip playfield line if larger row than allowed
7265     if (y >= MAX_LEV_FIELDY)
7266       continue;
7267
7268     // start with leftmost tile column
7269     x = 0;
7270
7271     // read playfield elements from line
7272     for (line_ptr = line; *line_ptr; line_ptr++)
7273     {
7274       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7275
7276       // stop parsing playfield line if larger column than allowed
7277       if (x >= MAX_LEV_FIELDX)
7278         break;
7279
7280       if (mapped_sb_element == EL_UNDEFINED)
7281       {
7282         invalid_playfield_char = TRUE;
7283
7284         break;
7285       }
7286
7287       level->field[x][y] = mapped_sb_element;
7288
7289       // continue with next tile column
7290       x++;
7291
7292       level->fieldx = MAX(x, level->fieldx);
7293     }
7294
7295     if (invalid_playfield_char)
7296     {
7297       // if first playfield line, treat invalid lines as comment lines
7298       if (y == 0)
7299         reading_playfield = FALSE;
7300
7301       continue;
7302     }
7303
7304     // continue with next tile row
7305     y++;
7306   }
7307
7308   closeFile(file);
7309
7310   level->fieldy = y;
7311
7312   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7313   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7314
7315   if (!reading_playfield)
7316   {
7317     level->no_valid_file = TRUE;
7318
7319     Warn("cannot read level '%s' -- using empty level", filename);
7320
7321     return;
7322   }
7323
7324   if (*level_name != '\0')
7325   {
7326     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7327     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7328   }
7329   else if (*last_comment != '\0')
7330   {
7331     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7332     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7333   }
7334   else
7335   {
7336     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7337   }
7338
7339   // set all empty fields beyond the border walls to invisible steel wall
7340   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7341   {
7342     if ((x == 0 || x == level->fieldx - 1 ||
7343          y == 0 || y == level->fieldy - 1) &&
7344         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7345       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7346                      level->field, level->fieldx, level->fieldy);
7347   }
7348
7349   // set special level settings for Sokoban levels
7350   SetLevelSettings_SB(level);
7351
7352   if (load_xsb_to_ces)
7353   {
7354     // special global settings can now be set in level template
7355     level->use_custom_template = TRUE;
7356   }
7357 }
7358
7359
7360 // -------------------------------------------------------------------------
7361 // functions for handling native levels
7362 // -------------------------------------------------------------------------
7363
7364 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7365                                      struct LevelFileInfo *level_file_info,
7366                                      boolean level_info_only)
7367 {
7368   int pos = 0;
7369
7370   // determine position of requested level inside level package
7371   if (level_file_info->packed)
7372     pos = level_file_info->nr - leveldir_current->first_level;
7373
7374   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7375     level->no_valid_file = TRUE;
7376 }
7377
7378 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7379                                      struct LevelFileInfo *level_file_info,
7380                                      boolean level_info_only)
7381 {
7382   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7383     level->no_valid_file = TRUE;
7384 }
7385
7386 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7387                                      struct LevelFileInfo *level_file_info,
7388                                      boolean level_info_only)
7389 {
7390   int pos = 0;
7391
7392   // determine position of requested level inside level package
7393   if (level_file_info->packed)
7394     pos = level_file_info->nr - leveldir_current->first_level;
7395
7396   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7397     level->no_valid_file = TRUE;
7398 }
7399
7400 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7401                                      struct LevelFileInfo *level_file_info,
7402                                      boolean level_info_only)
7403 {
7404   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7405     level->no_valid_file = TRUE;
7406 }
7407
7408 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7409 {
7410   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7411     CopyNativeLevel_RND_to_BD(level);
7412   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7413     CopyNativeLevel_RND_to_EM(level);
7414   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7415     CopyNativeLevel_RND_to_SP(level);
7416   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7417     CopyNativeLevel_RND_to_MM(level);
7418 }
7419
7420 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7421 {
7422   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7423     CopyNativeLevel_BD_to_RND(level);
7424   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7425     CopyNativeLevel_EM_to_RND(level);
7426   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7427     CopyNativeLevel_SP_to_RND(level);
7428   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7429     CopyNativeLevel_MM_to_RND(level);
7430 }
7431
7432 void SaveNativeLevel(struct LevelInfo *level)
7433 {
7434   // saving native level files only supported for some game engines
7435   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7436       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7437     return;
7438
7439   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7440                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7441   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7442   char *filename = getLevelFilenameFromBasename(basename);
7443
7444   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7445     return;
7446
7447   boolean success = FALSE;
7448
7449   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7450   {
7451     CopyNativeLevel_RND_to_BD(level);
7452     // CopyNativeTape_RND_to_BD(level);
7453
7454     success = SaveNativeLevel_BD(filename);
7455   }
7456   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7457   {
7458     CopyNativeLevel_RND_to_SP(level);
7459     CopyNativeTape_RND_to_SP(level);
7460
7461     success = SaveNativeLevel_SP(filename);
7462   }
7463
7464   if (success)
7465     Request("Native level file saved!", REQ_CONFIRM);
7466   else
7467     Request("Failed to save native level file!", REQ_CONFIRM);
7468 }
7469
7470
7471 // ----------------------------------------------------------------------------
7472 // functions for loading generic level
7473 // ----------------------------------------------------------------------------
7474
7475 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7476                                   struct LevelFileInfo *level_file_info,
7477                                   boolean level_info_only)
7478 {
7479   // always start with reliable default values
7480   setLevelInfoToDefaults(level, level_info_only, TRUE);
7481
7482   switch (level_file_info->type)
7483   {
7484     case LEVEL_FILE_TYPE_RND:
7485       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7486       break;
7487
7488     case LEVEL_FILE_TYPE_BD:
7489       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7490       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7491       break;
7492
7493     case LEVEL_FILE_TYPE_EM:
7494       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7495       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7496       break;
7497
7498     case LEVEL_FILE_TYPE_SP:
7499       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7500       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7501       break;
7502
7503     case LEVEL_FILE_TYPE_MM:
7504       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7505       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7506       break;
7507
7508     case LEVEL_FILE_TYPE_DC:
7509       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7510       break;
7511
7512     case LEVEL_FILE_TYPE_SB:
7513       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7514       break;
7515
7516     default:
7517       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7518       break;
7519   }
7520
7521   // if level file is invalid, restore level structure to default values
7522   if (level->no_valid_file)
7523     setLevelInfoToDefaults(level, level_info_only, FALSE);
7524
7525   if (check_special_flags("use_native_bd_game_engine"))
7526     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7527
7528   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7529     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7530
7531   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7532     CopyNativeLevel_Native_to_RND(level);
7533 }
7534
7535 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7536 {
7537   static struct LevelFileInfo level_file_info;
7538
7539   // always start with reliable default values
7540   setFileInfoToDefaults(&level_file_info);
7541
7542   level_file_info.nr = 0;                       // unknown level number
7543   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7544
7545   setString(&level_file_info.filename, filename);
7546
7547   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7548 }
7549
7550 static void LoadLevel_InitVersion(struct LevelInfo *level)
7551 {
7552   int i, j;
7553
7554   if (leveldir_current == NULL)         // only when dumping level
7555     return;
7556
7557   // all engine modifications also valid for levels which use latest engine
7558   if (level->game_version < VERSION_IDENT(3,2,0,5))
7559   {
7560     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7561     level->time_score_base = 10;
7562   }
7563
7564   if (leveldir_current->latest_engine)
7565   {
7566     // ---------- use latest game engine --------------------------------------
7567
7568     /* For all levels which are forced to use the latest game engine version
7569        (normally all but user contributed, private and undefined levels), set
7570        the game engine version to the actual version; this allows for actual
7571        corrections in the game engine to take effect for existing, converted
7572        levels (from "classic" or other existing games) to make the emulation
7573        of the corresponding game more accurate, while (hopefully) not breaking
7574        existing levels created from other players. */
7575
7576     level->game_version = GAME_VERSION_ACTUAL;
7577
7578     /* Set special EM style gems behaviour: EM style gems slip down from
7579        normal, steel and growing wall. As this is a more fundamental change,
7580        it seems better to set the default behaviour to "off" (as it is more
7581        natural) and make it configurable in the level editor (as a property
7582        of gem style elements). Already existing converted levels (neither
7583        private nor contributed levels) are changed to the new behaviour. */
7584
7585     if (level->file_version < FILE_VERSION_2_0)
7586       level->em_slippery_gems = TRUE;
7587
7588     return;
7589   }
7590
7591   // ---------- use game engine the level was created with --------------------
7592
7593   /* For all levels which are not forced to use the latest game engine
7594      version (normally user contributed, private and undefined levels),
7595      use the version of the game engine the levels were created for.
7596
7597      Since 2.0.1, the game engine version is now directly stored
7598      in the level file (chunk "VERS"), so there is no need anymore
7599      to set the game version from the file version (except for old,
7600      pre-2.0 levels, where the game version is still taken from the
7601      file format version used to store the level -- see above). */
7602
7603   // player was faster than enemies in 1.0.0 and before
7604   if (level->file_version == FILE_VERSION_1_0)
7605     for (i = 0; i < MAX_PLAYERS; i++)
7606       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7607
7608   // default behaviour for EM style gems was "slippery" only in 2.0.1
7609   if (level->game_version == VERSION_IDENT(2,0,1,0))
7610     level->em_slippery_gems = TRUE;
7611
7612   // springs could be pushed over pits before (pre-release version) 2.2.0
7613   if (level->game_version < VERSION_IDENT(2,2,0,0))
7614     level->use_spring_bug = TRUE;
7615
7616   if (level->game_version < VERSION_IDENT(3,2,0,5))
7617   {
7618     // time orb caused limited time in endless time levels before 3.2.0-5
7619     level->use_time_orb_bug = TRUE;
7620
7621     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7622     level->block_snap_field = FALSE;
7623
7624     // extra time score was same value as time left score before 3.2.0-5
7625     level->extra_time_score = level->score[SC_TIME_BONUS];
7626   }
7627
7628   if (level->game_version < VERSION_IDENT(3,2,0,7))
7629   {
7630     // default behaviour for snapping was "not continuous" before 3.2.0-7
7631     level->continuous_snapping = FALSE;
7632   }
7633
7634   // only few elements were able to actively move into acid before 3.1.0
7635   // trigger settings did not exist before 3.1.0; set to default "any"
7636   if (level->game_version < VERSION_IDENT(3,1,0,0))
7637   {
7638     // correct "can move into acid" settings (all zero in old levels)
7639
7640     level->can_move_into_acid_bits = 0; // nothing can move into acid
7641     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7642
7643     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7644     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7645     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7646     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7647
7648     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7649       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7650
7651     // correct trigger settings (stored as zero == "none" in old levels)
7652
7653     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7654     {
7655       int element = EL_CUSTOM_START + i;
7656       struct ElementInfo *ei = &element_info[element];
7657
7658       for (j = 0; j < ei->num_change_pages; j++)
7659       {
7660         struct ElementChangeInfo *change = &ei->change_page[j];
7661
7662         change->trigger_player = CH_PLAYER_ANY;
7663         change->trigger_page = CH_PAGE_ANY;
7664       }
7665     }
7666   }
7667
7668   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7669   {
7670     int element = EL_CUSTOM_256;
7671     struct ElementInfo *ei = &element_info[element];
7672     struct ElementChangeInfo *change = &ei->change_page[0];
7673
7674     /* This is needed to fix a problem that was caused by a bugfix in function
7675        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7676        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7677        not replace walkable elements, but instead just placed the player on it,
7678        without placing the Sokoban field under the player). Unfortunately, this
7679        breaks "Snake Bite" style levels when the snake is halfway through a door
7680        that just closes (the snake head is still alive and can be moved in this
7681        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7682        player (without Sokoban element) which then gets killed as designed). */
7683
7684     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7685          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7686         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7687       change->target_element = EL_PLAYER_1;
7688   }
7689
7690   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7691   if (level->game_version < VERSION_IDENT(3,2,5,0))
7692   {
7693     /* This is needed to fix a problem that was caused by a bugfix in function
7694        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7695        corrects the behaviour when a custom element changes to another custom
7696        element with a higher element number that has change actions defined.
7697        Normally, only one change per frame is allowed for custom elements.
7698        Therefore, it is checked if a custom element already changed in the
7699        current frame; if it did, subsequent changes are suppressed.
7700        Unfortunately, this is only checked for element changes, but not for
7701        change actions, which are still executed. As the function above loops
7702        through all custom elements from lower to higher, an element change
7703        resulting in a lower CE number won't be checked again, while a target
7704        element with a higher number will also be checked, and potential change
7705        actions will get executed for this CE, too (which is wrong), while
7706        further changes are ignored (which is correct). As this bugfix breaks
7707        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7708        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7709        behaviour for existing levels and tapes that make use of this bug */
7710
7711     level->use_action_after_change_bug = TRUE;
7712   }
7713
7714   // not centering level after relocating player was default only in 3.2.3
7715   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7716     level->shifted_relocation = TRUE;
7717
7718   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7719   if (level->game_version < VERSION_IDENT(3,2,6,0))
7720     level->em_explodes_by_fire = TRUE;
7721
7722   // levels were solved by the first player entering an exit up to 4.1.0.0
7723   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7724     level->solved_by_one_player = TRUE;
7725
7726   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7727   if (level->game_version < VERSION_IDENT(4,1,1,1))
7728     level->use_life_bugs = TRUE;
7729
7730   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7731   if (level->game_version < VERSION_IDENT(4,1,1,1))
7732     level->sb_objects_needed = FALSE;
7733
7734   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7735   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7736     level->finish_dig_collect = FALSE;
7737
7738   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7739   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7740     level->keep_walkable_ce = TRUE;
7741 }
7742
7743 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7744 {
7745   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7746   int x, y;
7747
7748   // check if this level is (not) a Sokoban level
7749   for (y = 0; y < level->fieldy; y++)
7750     for (x = 0; x < level->fieldx; x++)
7751       if (!IS_SB_ELEMENT(Tile[x][y]))
7752         is_sokoban_level = FALSE;
7753
7754   if (is_sokoban_level)
7755   {
7756     // set special level settings for Sokoban levels
7757     SetLevelSettings_SB(level);
7758   }
7759 }
7760
7761 static void LoadLevel_InitSettings(struct LevelInfo *level)
7762 {
7763   // adjust level settings for (non-native) Sokoban-style levels
7764   LoadLevel_InitSettings_SB(level);
7765
7766   // rename levels with title "nameless level" or if renaming is forced
7767   if (leveldir_current->empty_level_name != NULL &&
7768       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7769        leveldir_current->force_level_name))
7770     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7771              leveldir_current->empty_level_name, level_nr);
7772 }
7773
7774 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7775 {
7776   int i, x, y;
7777
7778   // map elements that have changed in newer versions
7779   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7780                                                     level->game_version);
7781   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7782     for (x = 0; x < 3; x++)
7783       for (y = 0; y < 3; y++)
7784         level->yamyam_content[i].e[x][y] =
7785           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7786                                     level->game_version);
7787
7788 }
7789
7790 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7791 {
7792   int i, j;
7793
7794   // map custom element change events that have changed in newer versions
7795   // (these following values were accidentally changed in version 3.0.1)
7796   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7797   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7798   {
7799     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7800     {
7801       int element = EL_CUSTOM_START + i;
7802
7803       // order of checking and copying events to be mapped is important
7804       // (do not change the start and end value -- they are constant)
7805       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7806       {
7807         if (HAS_CHANGE_EVENT(element, j - 2))
7808         {
7809           SET_CHANGE_EVENT(element, j - 2, FALSE);
7810           SET_CHANGE_EVENT(element, j, TRUE);
7811         }
7812       }
7813
7814       // order of checking and copying events to be mapped is important
7815       // (do not change the start and end value -- they are constant)
7816       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7817       {
7818         if (HAS_CHANGE_EVENT(element, j - 1))
7819         {
7820           SET_CHANGE_EVENT(element, j - 1, FALSE);
7821           SET_CHANGE_EVENT(element, j, TRUE);
7822         }
7823       }
7824     }
7825   }
7826
7827   // initialize "can_change" field for old levels with only one change page
7828   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7829   {
7830     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7831     {
7832       int element = EL_CUSTOM_START + i;
7833
7834       if (CAN_CHANGE(element))
7835         element_info[element].change->can_change = TRUE;
7836     }
7837   }
7838
7839   // correct custom element values (for old levels without these options)
7840   if (level->game_version < VERSION_IDENT(3,1,1,0))
7841   {
7842     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7843     {
7844       int element = EL_CUSTOM_START + i;
7845       struct ElementInfo *ei = &element_info[element];
7846
7847       if (ei->access_direction == MV_NO_DIRECTION)
7848         ei->access_direction = MV_ALL_DIRECTIONS;
7849     }
7850   }
7851
7852   // correct custom element values (fix invalid values for all versions)
7853   if (1)
7854   {
7855     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7856     {
7857       int element = EL_CUSTOM_START + i;
7858       struct ElementInfo *ei = &element_info[element];
7859
7860       for (j = 0; j < ei->num_change_pages; j++)
7861       {
7862         struct ElementChangeInfo *change = &ei->change_page[j];
7863
7864         if (change->trigger_player == CH_PLAYER_NONE)
7865           change->trigger_player = CH_PLAYER_ANY;
7866
7867         if (change->trigger_side == CH_SIDE_NONE)
7868           change->trigger_side = CH_SIDE_ANY;
7869       }
7870     }
7871   }
7872
7873   // initialize "can_explode" field for old levels which did not store this
7874   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7875   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7876   {
7877     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7878     {
7879       int element = EL_CUSTOM_START + i;
7880
7881       if (EXPLODES_1X1_OLD(element))
7882         element_info[element].explosion_type = EXPLODES_1X1;
7883
7884       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7885                                              EXPLODES_SMASHED(element) ||
7886                                              EXPLODES_IMPACT(element)));
7887     }
7888   }
7889
7890   // correct previously hard-coded move delay values for maze runner style
7891   if (level->game_version < VERSION_IDENT(3,1,1,0))
7892   {
7893     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7894     {
7895       int element = EL_CUSTOM_START + i;
7896
7897       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7898       {
7899         // previously hard-coded and therefore ignored
7900         element_info[element].move_delay_fixed = 9;
7901         element_info[element].move_delay_random = 0;
7902       }
7903     }
7904   }
7905
7906   // set some other uninitialized values of custom elements in older levels
7907   if (level->game_version < VERSION_IDENT(3,1,0,0))
7908   {
7909     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7910     {
7911       int element = EL_CUSTOM_START + i;
7912
7913       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7914
7915       element_info[element].explosion_delay = 17;
7916       element_info[element].ignition_delay = 8;
7917     }
7918   }
7919
7920   // set mouse click change events to work for left/middle/right mouse button
7921   if (level->game_version < VERSION_IDENT(4,2,3,0))
7922   {
7923     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7924     {
7925       int element = EL_CUSTOM_START + i;
7926       struct ElementInfo *ei = &element_info[element];
7927
7928       for (j = 0; j < ei->num_change_pages; j++)
7929       {
7930         struct ElementChangeInfo *change = &ei->change_page[j];
7931
7932         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7933             change->has_event[CE_PRESSED_BY_MOUSE] ||
7934             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7935             change->has_event[CE_MOUSE_PRESSED_ON_X])
7936           change->trigger_side = CH_SIDE_ANY;
7937       }
7938     }
7939   }
7940 }
7941
7942 static void LoadLevel_InitElements(struct LevelInfo *level)
7943 {
7944   LoadLevel_InitStandardElements(level);
7945
7946   if (level->file_has_custom_elements)
7947     LoadLevel_InitCustomElements(level);
7948
7949   // initialize element properties for level editor etc.
7950   InitElementPropertiesEngine(level->game_version);
7951   InitElementPropertiesGfxElement();
7952 }
7953
7954 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7955 {
7956   int x, y;
7957
7958   // map elements that have changed in newer versions
7959   for (y = 0; y < level->fieldy; y++)
7960     for (x = 0; x < level->fieldx; x++)
7961       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7962                                                      level->game_version);
7963
7964   // clear unused playfield data (nicer if level gets resized in editor)
7965   for (x = 0; x < MAX_LEV_FIELDX; x++)
7966     for (y = 0; y < MAX_LEV_FIELDY; y++)
7967       if (x >= level->fieldx || y >= level->fieldy)
7968         level->field[x][y] = EL_EMPTY;
7969
7970   // copy elements to runtime playfield array
7971   for (x = 0; x < MAX_LEV_FIELDX; x++)
7972     for (y = 0; y < MAX_LEV_FIELDY; y++)
7973       Tile[x][y] = level->field[x][y];
7974
7975   // initialize level size variables for faster access
7976   lev_fieldx = level->fieldx;
7977   lev_fieldy = level->fieldy;
7978
7979   // determine border element for this level
7980   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7981     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7982   else
7983     SetBorderElement();
7984 }
7985
7986 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7987 {
7988   struct LevelFileInfo *level_file_info = &level->file_info;
7989
7990   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7991     CopyNativeLevel_RND_to_Native(level);
7992 }
7993
7994 static void LoadLevelTemplate_LoadAndInit(void)
7995 {
7996   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7997
7998   LoadLevel_InitVersion(&level_template);
7999   LoadLevel_InitElements(&level_template);
8000   LoadLevel_InitSettings(&level_template);
8001
8002   ActivateLevelTemplate();
8003 }
8004
8005 void LoadLevelTemplate(int nr)
8006 {
8007   if (!fileExists(getGlobalLevelTemplateFilename()))
8008   {
8009     Warn("no level template found for this level");
8010
8011     return;
8012   }
8013
8014   setLevelFileInfo(&level_template.file_info, nr);
8015
8016   LoadLevelTemplate_LoadAndInit();
8017 }
8018
8019 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8020 {
8021   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8022
8023   LoadLevelTemplate_LoadAndInit();
8024 }
8025
8026 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8027 {
8028   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8029
8030   if (level.use_custom_template)
8031   {
8032     if (network_level != NULL)
8033       LoadNetworkLevelTemplate(network_level);
8034     else
8035       LoadLevelTemplate(-1);
8036   }
8037
8038   LoadLevel_InitVersion(&level);
8039   LoadLevel_InitElements(&level);
8040   LoadLevel_InitPlayfield(&level);
8041   LoadLevel_InitSettings(&level);
8042
8043   LoadLevel_InitNativeEngines(&level);
8044 }
8045
8046 void LoadLevel(int nr)
8047 {
8048   SetLevelSetInfo(leveldir_current->identifier, nr);
8049
8050   setLevelFileInfo(&level.file_info, nr);
8051
8052   LoadLevel_LoadAndInit(NULL);
8053 }
8054
8055 void LoadLevelInfoOnly(int nr)
8056 {
8057   setLevelFileInfo(&level.file_info, nr);
8058
8059   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8060 }
8061
8062 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8063 {
8064   SetLevelSetInfo(network_level->leveldir_identifier,
8065                   network_level->file_info.nr);
8066
8067   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8068
8069   LoadLevel_LoadAndInit(network_level);
8070 }
8071
8072 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8073 {
8074   int chunk_size = 0;
8075
8076   chunk_size += putFileVersion(file, level->file_version);
8077   chunk_size += putFileVersion(file, level->game_version);
8078
8079   return chunk_size;
8080 }
8081
8082 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8083 {
8084   int chunk_size = 0;
8085
8086   chunk_size += putFile16BitBE(file, level->creation_date.year);
8087   chunk_size += putFile8Bit(file,    level->creation_date.month);
8088   chunk_size += putFile8Bit(file,    level->creation_date.day);
8089
8090   return chunk_size;
8091 }
8092
8093 #if ENABLE_HISTORIC_CHUNKS
8094 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8095 {
8096   int i, x, y;
8097
8098   putFile8Bit(file, level->fieldx);
8099   putFile8Bit(file, level->fieldy);
8100
8101   putFile16BitBE(file, level->time);
8102   putFile16BitBE(file, level->gems_needed);
8103
8104   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8105     putFile8Bit(file, level->name[i]);
8106
8107   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8108     putFile8Bit(file, level->score[i]);
8109
8110   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8111     for (y = 0; y < 3; y++)
8112       for (x = 0; x < 3; x++)
8113         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8114                            level->yamyam_content[i].e[x][y]));
8115   putFile8Bit(file, level->amoeba_speed);
8116   putFile8Bit(file, level->time_magic_wall);
8117   putFile8Bit(file, level->time_wheel);
8118   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8119                      level->amoeba_content));
8120   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8121   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8122   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8123   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8124
8125   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8126
8127   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8128   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8129   putFile32BitBE(file, level->can_move_into_acid_bits);
8130   putFile8Bit(file, level->dont_collide_with_bits);
8131
8132   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8133   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8134
8135   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8136   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8137   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8138
8139   putFile8Bit(file, level->game_engine_type);
8140
8141   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8142 }
8143 #endif
8144
8145 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8146 {
8147   int chunk_size = 0;
8148   int i;
8149
8150   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8151     chunk_size += putFile8Bit(file, level->name[i]);
8152
8153   return chunk_size;
8154 }
8155
8156 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8157 {
8158   int chunk_size = 0;
8159   int i;
8160
8161   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8162     chunk_size += putFile8Bit(file, level->author[i]);
8163
8164   return chunk_size;
8165 }
8166
8167 #if ENABLE_HISTORIC_CHUNKS
8168 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8169 {
8170   int chunk_size = 0;
8171   int x, y;
8172
8173   for (y = 0; y < level->fieldy; y++)
8174     for (x = 0; x < level->fieldx; x++)
8175       if (level->encoding_16bit_field)
8176         chunk_size += putFile16BitBE(file, level->field[x][y]);
8177       else
8178         chunk_size += putFile8Bit(file, level->field[x][y]);
8179
8180   return chunk_size;
8181 }
8182 #endif
8183
8184 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8185 {
8186   int chunk_size = 0;
8187   int x, y;
8188
8189   for (y = 0; y < level->fieldy; y++) 
8190     for (x = 0; x < level->fieldx; x++) 
8191       chunk_size += putFile16BitBE(file, level->field[x][y]);
8192
8193   return chunk_size;
8194 }
8195
8196 #if ENABLE_HISTORIC_CHUNKS
8197 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8198 {
8199   int i, x, y;
8200
8201   putFile8Bit(file, EL_YAMYAM);
8202   putFile8Bit(file, level->num_yamyam_contents);
8203   putFile8Bit(file, 0);
8204   putFile8Bit(file, 0);
8205
8206   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8207     for (y = 0; y < 3; y++)
8208       for (x = 0; x < 3; x++)
8209         if (level->encoding_16bit_field)
8210           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8211         else
8212           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8213 }
8214 #endif
8215
8216 #if ENABLE_HISTORIC_CHUNKS
8217 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8218 {
8219   int i, x, y;
8220   int num_contents, content_xsize, content_ysize;
8221   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8222
8223   if (element == EL_YAMYAM)
8224   {
8225     num_contents = level->num_yamyam_contents;
8226     content_xsize = 3;
8227     content_ysize = 3;
8228
8229     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8230       for (y = 0; y < 3; y++)
8231         for (x = 0; x < 3; x++)
8232           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8233   }
8234   else if (element == EL_BD_AMOEBA)
8235   {
8236     num_contents = 1;
8237     content_xsize = 1;
8238     content_ysize = 1;
8239
8240     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8241       for (y = 0; y < 3; y++)
8242         for (x = 0; x < 3; x++)
8243           content_array[i][x][y] = EL_EMPTY;
8244     content_array[0][0][0] = level->amoeba_content;
8245   }
8246   else
8247   {
8248     // chunk header already written -- write empty chunk data
8249     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8250
8251     Warn("cannot save content for element '%d'", element);
8252
8253     return;
8254   }
8255
8256   putFile16BitBE(file, element);
8257   putFile8Bit(file, num_contents);
8258   putFile8Bit(file, content_xsize);
8259   putFile8Bit(file, content_ysize);
8260
8261   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8262
8263   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8264     for (y = 0; y < 3; y++)
8265       for (x = 0; x < 3; x++)
8266         putFile16BitBE(file, content_array[i][x][y]);
8267 }
8268 #endif
8269
8270 #if ENABLE_HISTORIC_CHUNKS
8271 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8272 {
8273   int envelope_nr = element - EL_ENVELOPE_1;
8274   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8275   int chunk_size = 0;
8276   int i;
8277
8278   chunk_size += putFile16BitBE(file, element);
8279   chunk_size += putFile16BitBE(file, envelope_len);
8280   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8281   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8282
8283   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8284   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8285
8286   for (i = 0; i < envelope_len; i++)
8287     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8288
8289   return chunk_size;
8290 }
8291 #endif
8292
8293 #if ENABLE_HISTORIC_CHUNKS
8294 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8295                            int num_changed_custom_elements)
8296 {
8297   int i, check = 0;
8298
8299   putFile16BitBE(file, num_changed_custom_elements);
8300
8301   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8302   {
8303     int element = EL_CUSTOM_START + i;
8304
8305     struct ElementInfo *ei = &element_info[element];
8306
8307     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8308     {
8309       if (check < num_changed_custom_elements)
8310       {
8311         putFile16BitBE(file, element);
8312         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8313       }
8314
8315       check++;
8316     }
8317   }
8318
8319   if (check != num_changed_custom_elements)     // should not happen
8320     Warn("inconsistent number of custom element properties");
8321 }
8322 #endif
8323
8324 #if ENABLE_HISTORIC_CHUNKS
8325 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8326                            int num_changed_custom_elements)
8327 {
8328   int i, check = 0;
8329
8330   putFile16BitBE(file, num_changed_custom_elements);
8331
8332   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8333   {
8334     int element = EL_CUSTOM_START + i;
8335
8336     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8337     {
8338       if (check < num_changed_custom_elements)
8339       {
8340         putFile16BitBE(file, element);
8341         putFile16BitBE(file, element_info[element].change->target_element);
8342       }
8343
8344       check++;
8345     }
8346   }
8347
8348   if (check != num_changed_custom_elements)     // should not happen
8349     Warn("inconsistent number of custom target elements");
8350 }
8351 #endif
8352
8353 #if ENABLE_HISTORIC_CHUNKS
8354 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8355                            int num_changed_custom_elements)
8356 {
8357   int i, j, x, y, check = 0;
8358
8359   putFile16BitBE(file, num_changed_custom_elements);
8360
8361   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8362   {
8363     int element = EL_CUSTOM_START + i;
8364     struct ElementInfo *ei = &element_info[element];
8365
8366     if (ei->modified_settings)
8367     {
8368       if (check < num_changed_custom_elements)
8369       {
8370         putFile16BitBE(file, element);
8371
8372         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8373           putFile8Bit(file, ei->description[j]);
8374
8375         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8376
8377         // some free bytes for future properties and padding
8378         WriteUnusedBytesToFile(file, 7);
8379
8380         putFile8Bit(file, ei->use_gfx_element);
8381         putFile16BitBE(file, ei->gfx_element_initial);
8382
8383         putFile8Bit(file, ei->collect_score_initial);
8384         putFile8Bit(file, ei->collect_count_initial);
8385
8386         putFile16BitBE(file, ei->push_delay_fixed);
8387         putFile16BitBE(file, ei->push_delay_random);
8388         putFile16BitBE(file, ei->move_delay_fixed);
8389         putFile16BitBE(file, ei->move_delay_random);
8390
8391         putFile16BitBE(file, ei->move_pattern);
8392         putFile8Bit(file, ei->move_direction_initial);
8393         putFile8Bit(file, ei->move_stepsize);
8394
8395         for (y = 0; y < 3; y++)
8396           for (x = 0; x < 3; x++)
8397             putFile16BitBE(file, ei->content.e[x][y]);
8398
8399         putFile32BitBE(file, ei->change->events);
8400
8401         putFile16BitBE(file, ei->change->target_element);
8402
8403         putFile16BitBE(file, ei->change->delay_fixed);
8404         putFile16BitBE(file, ei->change->delay_random);
8405         putFile16BitBE(file, ei->change->delay_frames);
8406
8407         putFile16BitBE(file, ei->change->initial_trigger_element);
8408
8409         putFile8Bit(file, ei->change->explode);
8410         putFile8Bit(file, ei->change->use_target_content);
8411         putFile8Bit(file, ei->change->only_if_complete);
8412         putFile8Bit(file, ei->change->use_random_replace);
8413
8414         putFile8Bit(file, ei->change->random_percentage);
8415         putFile8Bit(file, ei->change->replace_when);
8416
8417         for (y = 0; y < 3; y++)
8418           for (x = 0; x < 3; x++)
8419             putFile16BitBE(file, ei->change->content.e[x][y]);
8420
8421         putFile8Bit(file, ei->slippery_type);
8422
8423         // some free bytes for future properties and padding
8424         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8425       }
8426
8427       check++;
8428     }
8429   }
8430
8431   if (check != num_changed_custom_elements)     // should not happen
8432     Warn("inconsistent number of custom element properties");
8433 }
8434 #endif
8435
8436 #if ENABLE_HISTORIC_CHUNKS
8437 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8438 {
8439   struct ElementInfo *ei = &element_info[element];
8440   int i, j, x, y;
8441
8442   // ---------- custom element base property values (96 bytes) ----------------
8443
8444   putFile16BitBE(file, element);
8445
8446   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8447     putFile8Bit(file, ei->description[i]);
8448
8449   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8450
8451   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8452
8453   putFile8Bit(file, ei->num_change_pages);
8454
8455   putFile16BitBE(file, ei->ce_value_fixed_initial);
8456   putFile16BitBE(file, ei->ce_value_random_initial);
8457   putFile8Bit(file, ei->use_last_ce_value);
8458
8459   putFile8Bit(file, ei->use_gfx_element);
8460   putFile16BitBE(file, ei->gfx_element_initial);
8461
8462   putFile8Bit(file, ei->collect_score_initial);
8463   putFile8Bit(file, ei->collect_count_initial);
8464
8465   putFile8Bit(file, ei->drop_delay_fixed);
8466   putFile8Bit(file, ei->push_delay_fixed);
8467   putFile8Bit(file, ei->drop_delay_random);
8468   putFile8Bit(file, ei->push_delay_random);
8469   putFile16BitBE(file, ei->move_delay_fixed);
8470   putFile16BitBE(file, ei->move_delay_random);
8471
8472   // bits 0 - 15 of "move_pattern" ...
8473   putFile16BitBE(file, ei->move_pattern & 0xffff);
8474   putFile8Bit(file, ei->move_direction_initial);
8475   putFile8Bit(file, ei->move_stepsize);
8476
8477   putFile8Bit(file, ei->slippery_type);
8478
8479   for (y = 0; y < 3; y++)
8480     for (x = 0; x < 3; x++)
8481       putFile16BitBE(file, ei->content.e[x][y]);
8482
8483   putFile16BitBE(file, ei->move_enter_element);
8484   putFile16BitBE(file, ei->move_leave_element);
8485   putFile8Bit(file, ei->move_leave_type);
8486
8487   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8488   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8489
8490   putFile8Bit(file, ei->access_direction);
8491
8492   putFile8Bit(file, ei->explosion_delay);
8493   putFile8Bit(file, ei->ignition_delay);
8494   putFile8Bit(file, ei->explosion_type);
8495
8496   // some free bytes for future custom property values and padding
8497   WriteUnusedBytesToFile(file, 1);
8498
8499   // ---------- change page property values (48 bytes) ------------------------
8500
8501   for (i = 0; i < ei->num_change_pages; i++)
8502   {
8503     struct ElementChangeInfo *change = &ei->change_page[i];
8504     unsigned int event_bits;
8505
8506     // bits 0 - 31 of "has_event[]" ...
8507     event_bits = 0;
8508     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8509       if (change->has_event[j])
8510         event_bits |= (1u << j);
8511     putFile32BitBE(file, event_bits);
8512
8513     putFile16BitBE(file, change->target_element);
8514
8515     putFile16BitBE(file, change->delay_fixed);
8516     putFile16BitBE(file, change->delay_random);
8517     putFile16BitBE(file, change->delay_frames);
8518
8519     putFile16BitBE(file, change->initial_trigger_element);
8520
8521     putFile8Bit(file, change->explode);
8522     putFile8Bit(file, change->use_target_content);
8523     putFile8Bit(file, change->only_if_complete);
8524     putFile8Bit(file, change->use_random_replace);
8525
8526     putFile8Bit(file, change->random_percentage);
8527     putFile8Bit(file, change->replace_when);
8528
8529     for (y = 0; y < 3; y++)
8530       for (x = 0; x < 3; x++)
8531         putFile16BitBE(file, change->target_content.e[x][y]);
8532
8533     putFile8Bit(file, change->can_change);
8534
8535     putFile8Bit(file, change->trigger_side);
8536
8537     putFile8Bit(file, change->trigger_player);
8538     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8539                        log_2(change->trigger_page)));
8540
8541     putFile8Bit(file, change->has_action);
8542     putFile8Bit(file, change->action_type);
8543     putFile8Bit(file, change->action_mode);
8544     putFile16BitBE(file, change->action_arg);
8545
8546     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8547     event_bits = 0;
8548     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8549       if (change->has_event[j])
8550         event_bits |= (1u << (j - 32));
8551     putFile8Bit(file, event_bits);
8552   }
8553 }
8554 #endif
8555
8556 #if ENABLE_HISTORIC_CHUNKS
8557 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8558 {
8559   struct ElementInfo *ei = &element_info[element];
8560   struct ElementGroupInfo *group = ei->group;
8561   int i;
8562
8563   putFile16BitBE(file, element);
8564
8565   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8566     putFile8Bit(file, ei->description[i]);
8567
8568   putFile8Bit(file, group->num_elements);
8569
8570   putFile8Bit(file, ei->use_gfx_element);
8571   putFile16BitBE(file, ei->gfx_element_initial);
8572
8573   putFile8Bit(file, group->choice_mode);
8574
8575   // some free bytes for future values and padding
8576   WriteUnusedBytesToFile(file, 3);
8577
8578   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8579     putFile16BitBE(file, group->element[i]);
8580 }
8581 #endif
8582
8583 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8584                                 boolean write_element)
8585 {
8586   int save_type = entry->save_type;
8587   int data_type = entry->data_type;
8588   int conf_type = entry->conf_type;
8589   int byte_mask = conf_type & CONF_MASK_BYTES;
8590   int element = entry->element;
8591   int default_value = entry->default_value;
8592   int num_bytes = 0;
8593   boolean modified = FALSE;
8594
8595   if (byte_mask != CONF_MASK_MULTI_BYTES)
8596   {
8597     void *value_ptr = entry->value;
8598     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8599                  *(int *)value_ptr);
8600
8601     // check if any settings have been modified before saving them
8602     if (value != default_value)
8603       modified = TRUE;
8604
8605     // do not save if explicitly told or if unmodified default settings
8606     if ((save_type == SAVE_CONF_NEVER) ||
8607         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8608       return 0;
8609
8610     if (write_element)
8611       num_bytes += putFile16BitBE(file, element);
8612
8613     num_bytes += putFile8Bit(file, conf_type);
8614     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8615                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8616                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8617                   0);
8618   }
8619   else if (data_type == TYPE_STRING)
8620   {
8621     char *default_string = entry->default_string;
8622     char *string = (char *)(entry->value);
8623     int string_length = strlen(string);
8624     int i;
8625
8626     // check if any settings have been modified before saving them
8627     if (!strEqual(string, default_string))
8628       modified = TRUE;
8629
8630     // do not save if explicitly told or if unmodified default settings
8631     if ((save_type == SAVE_CONF_NEVER) ||
8632         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8633       return 0;
8634
8635     if (write_element)
8636       num_bytes += putFile16BitBE(file, element);
8637
8638     num_bytes += putFile8Bit(file, conf_type);
8639     num_bytes += putFile16BitBE(file, string_length);
8640
8641     for (i = 0; i < string_length; i++)
8642       num_bytes += putFile8Bit(file, string[i]);
8643   }
8644   else if (data_type == TYPE_ELEMENT_LIST)
8645   {
8646     int *element_array = (int *)(entry->value);
8647     int num_elements = *(int *)(entry->num_entities);
8648     int i;
8649
8650     // check if any settings have been modified before saving them
8651     for (i = 0; i < num_elements; i++)
8652       if (element_array[i] != default_value)
8653         modified = TRUE;
8654
8655     // do not save if explicitly told or if unmodified default settings
8656     if ((save_type == SAVE_CONF_NEVER) ||
8657         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8658       return 0;
8659
8660     if (write_element)
8661       num_bytes += putFile16BitBE(file, element);
8662
8663     num_bytes += putFile8Bit(file, conf_type);
8664     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8665
8666     for (i = 0; i < num_elements; i++)
8667       num_bytes += putFile16BitBE(file, element_array[i]);
8668   }
8669   else if (data_type == TYPE_CONTENT_LIST)
8670   {
8671     struct Content *content = (struct Content *)(entry->value);
8672     int num_contents = *(int *)(entry->num_entities);
8673     int i, x, y;
8674
8675     // check if any settings have been modified before saving them
8676     for (i = 0; i < num_contents; i++)
8677       for (y = 0; y < 3; y++)
8678         for (x = 0; x < 3; x++)
8679           if (content[i].e[x][y] != default_value)
8680             modified = TRUE;
8681
8682     // do not save if explicitly told or if unmodified default settings
8683     if ((save_type == SAVE_CONF_NEVER) ||
8684         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8685       return 0;
8686
8687     if (write_element)
8688       num_bytes += putFile16BitBE(file, element);
8689
8690     num_bytes += putFile8Bit(file, conf_type);
8691     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8692
8693     for (i = 0; i < num_contents; i++)
8694       for (y = 0; y < 3; y++)
8695         for (x = 0; x < 3; x++)
8696           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8697   }
8698
8699   return num_bytes;
8700 }
8701
8702 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8703 {
8704   int chunk_size = 0;
8705   int i;
8706
8707   li = *level;          // copy level data into temporary buffer
8708
8709   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8710     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8711
8712   return chunk_size;
8713 }
8714
8715 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8716 {
8717   int chunk_size = 0;
8718   int i;
8719
8720   li = *level;          // copy level data into temporary buffer
8721
8722   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8723     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8724
8725   return chunk_size;
8726 }
8727
8728 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8729 {
8730   int envelope_nr = element - EL_ENVELOPE_1;
8731   int chunk_size = 0;
8732   int i;
8733
8734   chunk_size += putFile16BitBE(file, element);
8735
8736   // copy envelope data into temporary buffer
8737   xx_envelope = level->envelope[envelope_nr];
8738
8739   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8740     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8741
8742   return chunk_size;
8743 }
8744
8745 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8746 {
8747   struct ElementInfo *ei = &element_info[element];
8748   int chunk_size = 0;
8749   int i, j;
8750
8751   chunk_size += putFile16BitBE(file, element);
8752
8753   xx_ei = *ei;          // copy element data into temporary buffer
8754
8755   // set default description string for this specific element
8756   strcpy(xx_default_description, getDefaultElementDescription(ei));
8757
8758   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8759     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8760
8761   for (i = 0; i < ei->num_change_pages; i++)
8762   {
8763     struct ElementChangeInfo *change = &ei->change_page[i];
8764
8765     xx_current_change_page = i;
8766
8767     xx_change = *change;        // copy change data into temporary buffer
8768
8769     resetEventBits();
8770     setEventBitsFromEventFlags(change);
8771
8772     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8773       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8774                                          FALSE);
8775   }
8776
8777   return chunk_size;
8778 }
8779
8780 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8781 {
8782   struct ElementInfo *ei = &element_info[element];
8783   struct ElementGroupInfo *group = ei->group;
8784   int chunk_size = 0;
8785   int i;
8786
8787   chunk_size += putFile16BitBE(file, element);
8788
8789   xx_ei = *ei;          // copy element data into temporary buffer
8790   xx_group = *group;    // copy group data into temporary buffer
8791
8792   // set default description string for this specific element
8793   strcpy(xx_default_description, getDefaultElementDescription(ei));
8794
8795   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8796     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8797
8798   return chunk_size;
8799 }
8800
8801 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8802 {
8803   struct ElementInfo *ei = &element_info[element];
8804   int chunk_size = 0;
8805   int i;
8806
8807   chunk_size += putFile16BitBE(file, element);
8808
8809   xx_ei = *ei;          // copy element data into temporary buffer
8810
8811   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8812     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8813
8814   return chunk_size;
8815 }
8816
8817 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8818                                   boolean save_as_template)
8819 {
8820   int chunk_size;
8821   int i;
8822   FILE *file;
8823
8824   if (!(file = fopen(filename, MODE_WRITE)))
8825   {
8826     Warn("cannot save level file '%s'", filename);
8827
8828     return;
8829   }
8830
8831   level->file_version = FILE_VERSION_ACTUAL;
8832   level->game_version = GAME_VERSION_ACTUAL;
8833
8834   level->creation_date = getCurrentDate();
8835
8836   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8837   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8838
8839   chunk_size = SaveLevel_VERS(NULL, level);
8840   putFileChunkBE(file, "VERS", chunk_size);
8841   SaveLevel_VERS(file, level);
8842
8843   chunk_size = SaveLevel_DATE(NULL, level);
8844   putFileChunkBE(file, "DATE", chunk_size);
8845   SaveLevel_DATE(file, level);
8846
8847   chunk_size = SaveLevel_NAME(NULL, level);
8848   putFileChunkBE(file, "NAME", chunk_size);
8849   SaveLevel_NAME(file, level);
8850
8851   chunk_size = SaveLevel_AUTH(NULL, level);
8852   putFileChunkBE(file, "AUTH", chunk_size);
8853   SaveLevel_AUTH(file, level);
8854
8855   chunk_size = SaveLevel_INFO(NULL, level);
8856   putFileChunkBE(file, "INFO", chunk_size);
8857   SaveLevel_INFO(file, level);
8858
8859   chunk_size = SaveLevel_BODY(NULL, level);
8860   putFileChunkBE(file, "BODY", chunk_size);
8861   SaveLevel_BODY(file, level);
8862
8863   chunk_size = SaveLevel_ELEM(NULL, level);
8864   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8865   {
8866     putFileChunkBE(file, "ELEM", chunk_size);
8867     SaveLevel_ELEM(file, level);
8868   }
8869
8870   for (i = 0; i < NUM_ENVELOPES; i++)
8871   {
8872     int element = EL_ENVELOPE_1 + i;
8873
8874     chunk_size = SaveLevel_NOTE(NULL, level, element);
8875     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8876     {
8877       putFileChunkBE(file, "NOTE", chunk_size);
8878       SaveLevel_NOTE(file, level, element);
8879     }
8880   }
8881
8882   // if not using template level, check for non-default custom/group elements
8883   if (!level->use_custom_template || save_as_template)
8884   {
8885     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8886     {
8887       int element = EL_CUSTOM_START + i;
8888
8889       chunk_size = SaveLevel_CUSX(NULL, level, element);
8890       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8891       {
8892         putFileChunkBE(file, "CUSX", chunk_size);
8893         SaveLevel_CUSX(file, level, element);
8894       }
8895     }
8896
8897     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8898     {
8899       int element = EL_GROUP_START + i;
8900
8901       chunk_size = SaveLevel_GRPX(NULL, level, element);
8902       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8903       {
8904         putFileChunkBE(file, "GRPX", chunk_size);
8905         SaveLevel_GRPX(file, level, element);
8906       }
8907     }
8908
8909     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8910     {
8911       int element = GET_EMPTY_ELEMENT(i);
8912
8913       chunk_size = SaveLevel_EMPX(NULL, level, element);
8914       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8915       {
8916         putFileChunkBE(file, "EMPX", chunk_size);
8917         SaveLevel_EMPX(file, level, element);
8918       }
8919     }
8920   }
8921
8922   fclose(file);
8923
8924   SetFilePermissions(filename, PERMS_PRIVATE);
8925 }
8926
8927 void SaveLevel(int nr)
8928 {
8929   char *filename = getDefaultLevelFilename(nr);
8930
8931   SaveLevelFromFilename(&level, filename, FALSE);
8932 }
8933
8934 void SaveLevelTemplate(void)
8935 {
8936   char *filename = getLocalLevelTemplateFilename();
8937
8938   SaveLevelFromFilename(&level, filename, TRUE);
8939 }
8940
8941 boolean SaveLevelChecked(int nr)
8942 {
8943   char *filename = getDefaultLevelFilename(nr);
8944   boolean new_level = !fileExists(filename);
8945   boolean level_saved = FALSE;
8946
8947   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8948   {
8949     SaveLevel(nr);
8950
8951     if (new_level)
8952       Request("Level saved!", REQ_CONFIRM);
8953
8954     level_saved = TRUE;
8955   }
8956
8957   return level_saved;
8958 }
8959
8960 void DumpLevel(struct LevelInfo *level)
8961 {
8962   if (level->no_level_file || level->no_valid_file)
8963   {
8964     Warn("cannot dump -- no valid level file found");
8965
8966     return;
8967   }
8968
8969   PrintLine("-", 79);
8970   Print("Level xxx (file version %08d, game version %08d)\n",
8971         level->file_version, level->game_version);
8972   PrintLine("-", 79);
8973
8974   Print("Level author: '%s'\n", level->author);
8975   Print("Level title:  '%s'\n", level->name);
8976   Print("\n");
8977   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8978   Print("\n");
8979   Print("Level time:  %d seconds\n", level->time);
8980   Print("Gems needed: %d\n", level->gems_needed);
8981   Print("\n");
8982   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8983   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8984   Print("Time for light:      %d seconds\n", level->time_light);
8985   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8986   Print("\n");
8987   Print("Amoeba speed: %d\n", level->amoeba_speed);
8988   Print("\n");
8989
8990   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8991   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8992   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8993   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8994   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8995   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8996
8997   if (options.debug)
8998   {
8999     int i, j;
9000
9001     for (i = 0; i < NUM_ENVELOPES; i++)
9002     {
9003       char *text = level->envelope[i].text;
9004       int text_len = strlen(text);
9005       boolean has_text = FALSE;
9006
9007       for (j = 0; j < text_len; j++)
9008         if (text[j] != ' ' && text[j] != '\n')
9009           has_text = TRUE;
9010
9011       if (has_text)
9012       {
9013         Print("\n");
9014         Print("Envelope %d:\n'%s'\n", i + 1, text);
9015       }
9016     }
9017   }
9018
9019   PrintLine("-", 79);
9020 }
9021
9022 void DumpLevels(void)
9023 {
9024   static LevelDirTree *dumplevel_leveldir = NULL;
9025
9026   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9027                                                  global.dumplevel_leveldir);
9028
9029   if (dumplevel_leveldir == NULL)
9030     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9031
9032   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9033       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9034     Fail("no such level number: %d", global.dumplevel_level_nr);
9035
9036   leveldir_current = dumplevel_leveldir;
9037
9038   LoadLevel(global.dumplevel_level_nr);
9039   DumpLevel(&level);
9040
9041   CloseAllAndExit(0);
9042 }
9043
9044 void DumpLevelsetFromFilename_BD(char *filename)
9045 {
9046   if (leveldir_current == NULL) // no levelsets loaded yet
9047     bd_open_all();
9048
9049   if (!LoadNativeLevel_BD(filename, 0, FALSE))
9050     CloseAllAndExit(0);         // function has already printed warning
9051
9052   PrintLine("-", 79);
9053   Print("Levelset '%s'\n", filename);
9054   PrintLine("-", 79);
9055
9056   DumpLevelset_BD();
9057
9058   PrintLine("-", 79);
9059
9060   CloseAllAndExit(0);
9061 }
9062
9063 void DumpLevelset(void)
9064 {
9065   static LevelDirTree *dumplevelset_leveldir = NULL;
9066
9067   dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9068                                                     global.dumplevelset_leveldir);
9069   if (dumplevelset_leveldir == NULL)
9070     Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9071
9072   PrintLine("-", 79);
9073   Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9074   PrintLine("-", 79);
9075
9076   Print("Number of levels:   %d\n", dumplevelset_leveldir->levels);
9077   Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9078
9079   PrintLine("-", 79);
9080
9081   CloseAllAndExit(0);
9082 }
9083
9084
9085 // ============================================================================
9086 // tape file functions
9087 // ============================================================================
9088
9089 static void setTapeInfoToDefaults(void)
9090 {
9091   int i;
9092
9093   // always start with reliable default values (empty tape)
9094   TapeErase();
9095
9096   // default values (also for pre-1.2 tapes) with only the first player
9097   tape.player_participates[0] = TRUE;
9098   for (i = 1; i < MAX_PLAYERS; i++)
9099     tape.player_participates[i] = FALSE;
9100
9101   // at least one (default: the first) player participates in every tape
9102   tape.num_participating_players = 1;
9103
9104   tape.property_bits = TAPE_PROPERTY_NONE;
9105
9106   tape.level_nr = level_nr;
9107   tape.counter = 0;
9108   tape.changed = FALSE;
9109   tape.solved = FALSE;
9110
9111   tape.recording = FALSE;
9112   tape.playing = FALSE;
9113   tape.pausing = FALSE;
9114
9115   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9116   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9117
9118   tape.no_info_chunk = TRUE;
9119   tape.no_valid_file = FALSE;
9120 }
9121
9122 static int getTapePosSize(struct TapeInfo *tape)
9123 {
9124   int tape_pos_size = 0;
9125
9126   if (tape->use_key_actions)
9127     tape_pos_size += tape->num_participating_players;
9128
9129   if (tape->use_mouse_actions)
9130     tape_pos_size += 3;         // x and y position and mouse button mask
9131
9132   tape_pos_size += 1;           // tape action delay value
9133
9134   return tape_pos_size;
9135 }
9136
9137 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9138 {
9139   tape->use_key_actions = FALSE;
9140   tape->use_mouse_actions = FALSE;
9141
9142   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9143     tape->use_key_actions = TRUE;
9144
9145   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9146     tape->use_mouse_actions = TRUE;
9147 }
9148
9149 static int getTapeActionValue(struct TapeInfo *tape)
9150 {
9151   return (tape->use_key_actions &&
9152           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9153           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9154           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9155           TAPE_ACTIONS_DEFAULT);
9156 }
9157
9158 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9159 {
9160   tape->file_version = getFileVersion(file);
9161   tape->game_version = getFileVersion(file);
9162
9163   return chunk_size;
9164 }
9165
9166 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9167 {
9168   int i;
9169
9170   tape->random_seed = getFile32BitBE(file);
9171   tape->date        = getFile32BitBE(file);
9172   tape->length      = getFile32BitBE(file);
9173
9174   // read header fields that are new since version 1.2
9175   if (tape->file_version >= FILE_VERSION_1_2)
9176   {
9177     byte store_participating_players = getFile8Bit(file);
9178     int engine_version;
9179
9180     // since version 1.2, tapes store which players participate in the tape
9181     tape->num_participating_players = 0;
9182     for (i = 0; i < MAX_PLAYERS; i++)
9183     {
9184       tape->player_participates[i] = FALSE;
9185
9186       if (store_participating_players & (1 << i))
9187       {
9188         tape->player_participates[i] = TRUE;
9189         tape->num_participating_players++;
9190       }
9191     }
9192
9193     setTapeActionFlags(tape, getFile8Bit(file));
9194
9195     tape->property_bits = getFile8Bit(file);
9196     tape->solved = getFile8Bit(file);
9197
9198     engine_version = getFileVersion(file);
9199     if (engine_version > 0)
9200       tape->engine_version = engine_version;
9201     else
9202       tape->engine_version = tape->game_version;
9203   }
9204
9205   return chunk_size;
9206 }
9207
9208 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9209 {
9210   tape->scr_fieldx = getFile8Bit(file);
9211   tape->scr_fieldy = getFile8Bit(file);
9212
9213   return chunk_size;
9214 }
9215
9216 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9217 {
9218   char *level_identifier = NULL;
9219   int level_identifier_size;
9220   int i;
9221
9222   tape->no_info_chunk = FALSE;
9223
9224   level_identifier_size = getFile16BitBE(file);
9225
9226   level_identifier = checked_malloc(level_identifier_size);
9227
9228   for (i = 0; i < level_identifier_size; i++)
9229     level_identifier[i] = getFile8Bit(file);
9230
9231   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9232   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9233
9234   checked_free(level_identifier);
9235
9236   tape->level_nr = getFile16BitBE(file);
9237
9238   chunk_size = 2 + level_identifier_size + 2;
9239
9240   return chunk_size;
9241 }
9242
9243 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9244 {
9245   int i, j;
9246   int tape_pos_size = getTapePosSize(tape);
9247   int chunk_size_expected = tape_pos_size * tape->length;
9248
9249   if (chunk_size_expected != chunk_size)
9250   {
9251     ReadUnusedBytesFromFile(file, chunk_size);
9252     return chunk_size_expected;
9253   }
9254
9255   for (i = 0; i < tape->length; i++)
9256   {
9257     if (i >= MAX_TAPE_LEN)
9258     {
9259       Warn("tape truncated -- size exceeds maximum tape size %d",
9260             MAX_TAPE_LEN);
9261
9262       // tape too large; read and ignore remaining tape data from this chunk
9263       for (;i < tape->length; i++)
9264         ReadUnusedBytesFromFile(file, tape_pos_size);
9265
9266       break;
9267     }
9268
9269     if (tape->use_key_actions)
9270     {
9271       for (j = 0; j < MAX_PLAYERS; j++)
9272       {
9273         tape->pos[i].action[j] = MV_NONE;
9274
9275         if (tape->player_participates[j])
9276           tape->pos[i].action[j] = getFile8Bit(file);
9277       }
9278     }
9279
9280     if (tape->use_mouse_actions)
9281     {
9282       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9283       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9284       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9285     }
9286
9287     tape->pos[i].delay = getFile8Bit(file);
9288
9289     if (tape->file_version == FILE_VERSION_1_0)
9290     {
9291       // eliminate possible diagonal moves in old tapes
9292       // this is only for backward compatibility
9293
9294       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9295       byte action = tape->pos[i].action[0];
9296       int k, num_moves = 0;
9297
9298       for (k = 0; k < 4; k++)
9299       {
9300         if (action & joy_dir[k])
9301         {
9302           tape->pos[i + num_moves].action[0] = joy_dir[k];
9303           if (num_moves > 0)
9304             tape->pos[i + num_moves].delay = 0;
9305           num_moves++;
9306         }
9307       }
9308
9309       if (num_moves > 1)
9310       {
9311         num_moves--;
9312         i += num_moves;
9313         tape->length += num_moves;
9314       }
9315     }
9316     else if (tape->file_version < FILE_VERSION_2_0)
9317     {
9318       // convert pre-2.0 tapes to new tape format
9319
9320       if (tape->pos[i].delay > 1)
9321       {
9322         // action part
9323         tape->pos[i + 1] = tape->pos[i];
9324         tape->pos[i + 1].delay = 1;
9325
9326         // delay part
9327         for (j = 0; j < MAX_PLAYERS; j++)
9328           tape->pos[i].action[j] = MV_NONE;
9329         tape->pos[i].delay--;
9330
9331         i++;
9332         tape->length++;
9333       }
9334     }
9335
9336     if (checkEndOfFile(file))
9337       break;
9338   }
9339
9340   if (i != tape->length)
9341     chunk_size = tape_pos_size * i;
9342
9343   return chunk_size;
9344 }
9345
9346 static void LoadTape_SokobanSolution(char *filename)
9347 {
9348   File *file;
9349   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9350
9351   if (!(file = openFile(filename, MODE_READ)))
9352   {
9353     tape.no_valid_file = TRUE;
9354
9355     return;
9356   }
9357
9358   while (!checkEndOfFile(file))
9359   {
9360     unsigned char c = getByteFromFile(file);
9361
9362     if (checkEndOfFile(file))
9363       break;
9364
9365     switch (c)
9366     {
9367       case 'u':
9368       case 'U':
9369         tape.pos[tape.length].action[0] = MV_UP;
9370         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9371         tape.length++;
9372         break;
9373
9374       case 'd':
9375       case 'D':
9376         tape.pos[tape.length].action[0] = MV_DOWN;
9377         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9378         tape.length++;
9379         break;
9380
9381       case 'l':
9382       case 'L':
9383         tape.pos[tape.length].action[0] = MV_LEFT;
9384         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9385         tape.length++;
9386         break;
9387
9388       case 'r':
9389       case 'R':
9390         tape.pos[tape.length].action[0] = MV_RIGHT;
9391         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9392         tape.length++;
9393         break;
9394
9395       case '\n':
9396       case '\r':
9397       case '\t':
9398       case ' ':
9399         // ignore white-space characters
9400         break;
9401
9402       default:
9403         tape.no_valid_file = TRUE;
9404
9405         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9406
9407         break;
9408     }
9409   }
9410
9411   closeFile(file);
9412
9413   if (tape.no_valid_file)
9414     return;
9415
9416   tape.length_frames  = GetTapeLengthFrames();
9417   tape.length_seconds = GetTapeLengthSeconds();
9418 }
9419
9420 void LoadTapeFromFilename(char *filename)
9421 {
9422   char cookie[MAX_LINE_LEN];
9423   char chunk_name[CHUNK_ID_LEN + 1];
9424   File *file;
9425   int chunk_size;
9426
9427   // always start with reliable default values
9428   setTapeInfoToDefaults();
9429
9430   if (strSuffix(filename, ".sln"))
9431   {
9432     LoadTape_SokobanSolution(filename);
9433
9434     return;
9435   }
9436
9437   if (!(file = openFile(filename, MODE_READ)))
9438   {
9439     tape.no_valid_file = TRUE;
9440
9441     return;
9442   }
9443
9444   getFileChunkBE(file, chunk_name, NULL);
9445   if (strEqual(chunk_name, "RND1"))
9446   {
9447     getFile32BitBE(file);               // not used
9448
9449     getFileChunkBE(file, chunk_name, NULL);
9450     if (!strEqual(chunk_name, "TAPE"))
9451     {
9452       tape.no_valid_file = TRUE;
9453
9454       Warn("unknown format of tape file '%s'", filename);
9455
9456       closeFile(file);
9457
9458       return;
9459     }
9460   }
9461   else  // check for pre-2.0 file format with cookie string
9462   {
9463     strcpy(cookie, chunk_name);
9464     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9465       cookie[4] = '\0';
9466     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9467       cookie[strlen(cookie) - 1] = '\0';
9468
9469     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9470     {
9471       tape.no_valid_file = TRUE;
9472
9473       Warn("unknown format of tape file '%s'", filename);
9474
9475       closeFile(file);
9476
9477       return;
9478     }
9479
9480     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9481     {
9482       tape.no_valid_file = TRUE;
9483
9484       Warn("unsupported version of tape file '%s'", filename);
9485
9486       closeFile(file);
9487
9488       return;
9489     }
9490
9491     // pre-2.0 tape files have no game version, so use file version here
9492     tape.game_version = tape.file_version;
9493   }
9494
9495   if (tape.file_version < FILE_VERSION_1_2)
9496   {
9497     // tape files from versions before 1.2.0 without chunk structure
9498     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9499     LoadTape_BODY(file, 2 * tape.length,      &tape);
9500   }
9501   else
9502   {
9503     static struct
9504     {
9505       char *name;
9506       int size;
9507       int (*loader)(File *, int, struct TapeInfo *);
9508     }
9509     chunk_info[] =
9510     {
9511       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9512       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9513       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9514       { "INFO", -1,                     LoadTape_INFO },
9515       { "BODY", -1,                     LoadTape_BODY },
9516       {  NULL,  0,                      NULL }
9517     };
9518
9519     while (getFileChunkBE(file, chunk_name, &chunk_size))
9520     {
9521       int i = 0;
9522
9523       while (chunk_info[i].name != NULL &&
9524              !strEqual(chunk_name, chunk_info[i].name))
9525         i++;
9526
9527       if (chunk_info[i].name == NULL)
9528       {
9529         Warn("unknown chunk '%s' in tape file '%s'",
9530               chunk_name, filename);
9531
9532         ReadUnusedBytesFromFile(file, chunk_size);
9533       }
9534       else if (chunk_info[i].size != -1 &&
9535                chunk_info[i].size != chunk_size)
9536       {
9537         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9538               chunk_size, chunk_name, filename);
9539
9540         ReadUnusedBytesFromFile(file, chunk_size);
9541       }
9542       else
9543       {
9544         // call function to load this tape chunk
9545         int chunk_size_expected =
9546           (chunk_info[i].loader)(file, chunk_size, &tape);
9547
9548         // the size of some chunks cannot be checked before reading other
9549         // chunks first (like "HEAD" and "BODY") that contain some header
9550         // information, so check them here
9551         if (chunk_size_expected != chunk_size)
9552         {
9553           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9554                 chunk_size, chunk_name, filename);
9555         }
9556       }
9557     }
9558   }
9559
9560   closeFile(file);
9561
9562   tape.length_frames  = GetTapeLengthFrames();
9563   tape.length_seconds = GetTapeLengthSeconds();
9564
9565 #if 0
9566   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9567         tape.file_version);
9568   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9569         tape.game_version);
9570   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9571         tape.engine_version);
9572 #endif
9573 }
9574
9575 void LoadTape(int nr)
9576 {
9577   char *filename = getTapeFilename(nr);
9578
9579   LoadTapeFromFilename(filename);
9580 }
9581
9582 void LoadSolutionTape(int nr)
9583 {
9584   char *filename = getSolutionTapeFilename(nr);
9585
9586   LoadTapeFromFilename(filename);
9587
9588   if (TAPE_IS_EMPTY(tape))
9589   {
9590     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9591         level.native_bd_level->replay != NULL)
9592       CopyNativeTape_BD_to_RND(&level);
9593     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9594         level.native_sp_level->demo.is_available)
9595       CopyNativeTape_SP_to_RND(&level);
9596   }
9597 }
9598
9599 void LoadScoreTape(char *score_tape_basename, int nr)
9600 {
9601   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9602
9603   LoadTapeFromFilename(filename);
9604 }
9605
9606 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9607 {
9608   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9609
9610   LoadTapeFromFilename(filename);
9611 }
9612
9613 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9614 {
9615   // chunk required for team mode tapes with non-default screen size
9616   return (tape->num_participating_players > 1 &&
9617           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9618            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9619 }
9620
9621 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9622 {
9623   putFileVersion(file, tape->file_version);
9624   putFileVersion(file, tape->game_version);
9625 }
9626
9627 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9628 {
9629   int i;
9630   byte store_participating_players = 0;
9631
9632   // set bits for participating players for compact storage
9633   for (i = 0; i < MAX_PLAYERS; i++)
9634     if (tape->player_participates[i])
9635       store_participating_players |= (1 << i);
9636
9637   putFile32BitBE(file, tape->random_seed);
9638   putFile32BitBE(file, tape->date);
9639   putFile32BitBE(file, tape->length);
9640
9641   putFile8Bit(file, store_participating_players);
9642
9643   putFile8Bit(file, getTapeActionValue(tape));
9644
9645   putFile8Bit(file, tape->property_bits);
9646   putFile8Bit(file, tape->solved);
9647
9648   putFileVersion(file, tape->engine_version);
9649 }
9650
9651 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9652 {
9653   putFile8Bit(file, tape->scr_fieldx);
9654   putFile8Bit(file, tape->scr_fieldy);
9655 }
9656
9657 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9658 {
9659   int level_identifier_size = strlen(tape->level_identifier) + 1;
9660   int i;
9661
9662   putFile16BitBE(file, level_identifier_size);
9663
9664   for (i = 0; i < level_identifier_size; i++)
9665     putFile8Bit(file, tape->level_identifier[i]);
9666
9667   putFile16BitBE(file, tape->level_nr);
9668 }
9669
9670 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9671 {
9672   int i, j;
9673
9674   for (i = 0; i < tape->length; i++)
9675   {
9676     if (tape->use_key_actions)
9677     {
9678       for (j = 0; j < MAX_PLAYERS; j++)
9679         if (tape->player_participates[j])
9680           putFile8Bit(file, tape->pos[i].action[j]);
9681     }
9682
9683     if (tape->use_mouse_actions)
9684     {
9685       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9686       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9687       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9688     }
9689
9690     putFile8Bit(file, tape->pos[i].delay);
9691   }
9692 }
9693
9694 void SaveTapeToFilename(char *filename)
9695 {
9696   FILE *file;
9697   int tape_pos_size;
9698   int info_chunk_size;
9699   int body_chunk_size;
9700
9701   if (!(file = fopen(filename, MODE_WRITE)))
9702   {
9703     Warn("cannot save level recording file '%s'", filename);
9704
9705     return;
9706   }
9707
9708   tape_pos_size = getTapePosSize(&tape);
9709
9710   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9711   body_chunk_size = tape_pos_size * tape.length;
9712
9713   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9714   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9715
9716   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9717   SaveTape_VERS(file, &tape);
9718
9719   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9720   SaveTape_HEAD(file, &tape);
9721
9722   if (checkSaveTape_SCRN(&tape))
9723   {
9724     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9725     SaveTape_SCRN(file, &tape);
9726   }
9727
9728   putFileChunkBE(file, "INFO", info_chunk_size);
9729   SaveTape_INFO(file, &tape);
9730
9731   putFileChunkBE(file, "BODY", body_chunk_size);
9732   SaveTape_BODY(file, &tape);
9733
9734   fclose(file);
9735
9736   SetFilePermissions(filename, PERMS_PRIVATE);
9737 }
9738
9739 static void SaveTapeExt(char *filename)
9740 {
9741   int i;
9742
9743   tape.file_version = FILE_VERSION_ACTUAL;
9744   tape.game_version = GAME_VERSION_ACTUAL;
9745
9746   tape.num_participating_players = 0;
9747
9748   // count number of participating players
9749   for (i = 0; i < MAX_PLAYERS; i++)
9750     if (tape.player_participates[i])
9751       tape.num_participating_players++;
9752
9753   SaveTapeToFilename(filename);
9754
9755   tape.changed = FALSE;
9756 }
9757
9758 void SaveTape(int nr)
9759 {
9760   char *filename = getTapeFilename(nr);
9761
9762   InitTapeDirectory(leveldir_current->subdir);
9763
9764   SaveTapeExt(filename);
9765 }
9766
9767 void SaveScoreTape(int nr)
9768 {
9769   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9770
9771   // used instead of "leveldir_current->subdir" (for network games)
9772   InitScoreTapeDirectory(levelset.identifier, nr);
9773
9774   SaveTapeExt(filename);
9775 }
9776
9777 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9778                                   unsigned int req_state_added)
9779 {
9780   char *filename = getTapeFilename(nr);
9781   boolean new_tape = !fileExists(filename);
9782   boolean tape_saved = FALSE;
9783
9784   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9785   {
9786     SaveTape(nr);
9787
9788     if (new_tape)
9789       Request(msg_saved, REQ_CONFIRM | req_state_added);
9790
9791     tape_saved = TRUE;
9792   }
9793
9794   return tape_saved;
9795 }
9796
9797 boolean SaveTapeChecked(int nr)
9798 {
9799   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9800 }
9801
9802 boolean SaveTapeChecked_LevelSolved(int nr)
9803 {
9804   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9805                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9806 }
9807
9808 void DumpTape(struct TapeInfo *tape)
9809 {
9810   int tape_frame_counter;
9811   int i, j;
9812
9813   if (tape->no_valid_file)
9814   {
9815     Warn("cannot dump -- no valid tape file found");
9816
9817     return;
9818   }
9819
9820   PrintLine("-", 79);
9821
9822   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9823         tape->level_nr, tape->file_version, tape->game_version);
9824   Print("                  (effective engine version %08d)\n",
9825         tape->engine_version);
9826   Print("Level series identifier: '%s'\n", tape->level_identifier);
9827
9828   Print("Solution tape: %s\n",
9829         tape->solved ? "yes" :
9830         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9831
9832   Print("Special tape properties: ");
9833   if (tape->property_bits == TAPE_PROPERTY_NONE)
9834     Print("[none]");
9835   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9836     Print("[em_random_bug]");
9837   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9838     Print("[game_speed]");
9839   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9840     Print("[pause]");
9841   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9842     Print("[single_step]");
9843   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9844     Print("[snapshot]");
9845   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9846     Print("[replayed]");
9847   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9848     Print("[tas_keys]");
9849   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9850     Print("[small_graphics]");
9851   Print("\n");
9852
9853   int year2 = tape->date / 10000;
9854   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9855   int month_index_raw = (tape->date / 100) % 100;
9856   int month_index = month_index_raw % 12;       // prevent invalid index
9857   int month = month_index + 1;
9858   int day = tape->date % 100;
9859
9860   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9861
9862   PrintLine("-", 79);
9863
9864   tape_frame_counter = 0;
9865
9866   for (i = 0; i < tape->length; i++)
9867   {
9868     if (i >= MAX_TAPE_LEN)
9869       break;
9870
9871     Print("%04d: ", i);
9872
9873     for (j = 0; j < MAX_PLAYERS; j++)
9874     {
9875       if (tape->player_participates[j])
9876       {
9877         int action = tape->pos[i].action[j];
9878
9879         Print("%d:%02x ", j, action);
9880         Print("[%c%c%c%c|%c%c] - ",
9881               (action & JOY_LEFT ? '<' : ' '),
9882               (action & JOY_RIGHT ? '>' : ' '),
9883               (action & JOY_UP ? '^' : ' '),
9884               (action & JOY_DOWN ? 'v' : ' '),
9885               (action & JOY_BUTTON_1 ? '1' : ' '),
9886               (action & JOY_BUTTON_2 ? '2' : ' '));
9887       }
9888     }
9889
9890     Print("(%03d) ", tape->pos[i].delay);
9891     Print("[%05d]\n", tape_frame_counter);
9892
9893     tape_frame_counter += tape->pos[i].delay;
9894   }
9895
9896   PrintLine("-", 79);
9897 }
9898
9899 void DumpTapes(void)
9900 {
9901   static LevelDirTree *dumptape_leveldir = NULL;
9902
9903   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9904                                                 global.dumptape_leveldir);
9905
9906   if (dumptape_leveldir == NULL)
9907     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9908
9909   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9910       global.dumptape_level_nr > dumptape_leveldir->last_level)
9911     Fail("no such level number: %d", global.dumptape_level_nr);
9912
9913   leveldir_current = dumptape_leveldir;
9914
9915   if (options.mytapes)
9916     LoadTape(global.dumptape_level_nr);
9917   else
9918     LoadSolutionTape(global.dumptape_level_nr);
9919
9920   DumpTape(&tape);
9921
9922   CloseAllAndExit(0);
9923 }
9924
9925
9926 // ============================================================================
9927 // score file functions
9928 // ============================================================================
9929
9930 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9931 {
9932   int i;
9933
9934   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9935   {
9936     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9937     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9938     scores->entry[i].score = 0;
9939     scores->entry[i].time = 0;
9940
9941     scores->entry[i].id = -1;
9942     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9943     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9944     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9945     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9946     strcpy(scores->entry[i].country_code, "??");
9947   }
9948
9949   scores->num_entries = 0;
9950   scores->last_added = -1;
9951   scores->last_added_local = -1;
9952
9953   scores->updated = FALSE;
9954   scores->uploaded = FALSE;
9955   scores->tape_downloaded = FALSE;
9956   scores->force_last_added = FALSE;
9957
9958   // The following values are intentionally not reset here:
9959   // - last_level_nr
9960   // - last_entry_nr
9961   // - next_level_nr
9962   // - continue_playing
9963   // - continue_on_return
9964 }
9965
9966 static void setScoreInfoToDefaults(void)
9967 {
9968   setScoreInfoToDefaultsExt(&scores);
9969 }
9970
9971 static void setServerScoreInfoToDefaults(void)
9972 {
9973   setScoreInfoToDefaultsExt(&server_scores);
9974 }
9975
9976 static void LoadScore_OLD(int nr)
9977 {
9978   int i;
9979   char *filename = getScoreFilename(nr);
9980   char cookie[MAX_LINE_LEN];
9981   char line[MAX_LINE_LEN];
9982   char *line_ptr;
9983   FILE *file;
9984
9985   if (!(file = fopen(filename, MODE_READ)))
9986     return;
9987
9988   // check file identifier
9989   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9990     cookie[0] = '\0';
9991   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9992     cookie[strlen(cookie) - 1] = '\0';
9993
9994   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9995   {
9996     Warn("unknown format of score file '%s'", filename);
9997
9998     fclose(file);
9999
10000     return;
10001   }
10002
10003   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10004   {
10005     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10006       Warn("fscanf() failed; %s", strerror(errno));
10007
10008     if (fgets(line, MAX_LINE_LEN, file) == NULL)
10009       line[0] = '\0';
10010
10011     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10012       line[strlen(line) - 1] = '\0';
10013
10014     for (line_ptr = line; *line_ptr; line_ptr++)
10015     {
10016       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10017       {
10018         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10019         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10020         break;
10021       }
10022     }
10023   }
10024
10025   fclose(file);
10026 }
10027
10028 static void ConvertScore_OLD(void)
10029 {
10030   // only convert score to time for levels that rate playing time over score
10031   if (!level.rate_time_over_score)
10032     return;
10033
10034   // convert old score to playing time for score-less levels (like Supaplex)
10035   int time_final_max = 999;
10036   int i;
10037
10038   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10039   {
10040     int score = scores.entry[i].score;
10041
10042     if (score > 0 && score < time_final_max)
10043       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10044   }
10045 }
10046
10047 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10048 {
10049   scores->file_version = getFileVersion(file);
10050   scores->game_version = getFileVersion(file);
10051
10052   return chunk_size;
10053 }
10054
10055 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10056 {
10057   char *level_identifier = NULL;
10058   int level_identifier_size;
10059   int i;
10060
10061   level_identifier_size = getFile16BitBE(file);
10062
10063   level_identifier = checked_malloc(level_identifier_size);
10064
10065   for (i = 0; i < level_identifier_size; i++)
10066     level_identifier[i] = getFile8Bit(file);
10067
10068   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10069   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10070
10071   checked_free(level_identifier);
10072
10073   scores->level_nr = getFile16BitBE(file);
10074   scores->num_entries = getFile16BitBE(file);
10075
10076   chunk_size = 2 + level_identifier_size + 2 + 2;
10077
10078   return chunk_size;
10079 }
10080
10081 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10082 {
10083   int i, j;
10084
10085   for (i = 0; i < scores->num_entries; i++)
10086   {
10087     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10088       scores->entry[i].name[j] = getFile8Bit(file);
10089
10090     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10091   }
10092
10093   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10094
10095   return chunk_size;
10096 }
10097
10098 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10099 {
10100   int i;
10101
10102   for (i = 0; i < scores->num_entries; i++)
10103     scores->entry[i].score = getFile16BitBE(file);
10104
10105   chunk_size = scores->num_entries * 2;
10106
10107   return chunk_size;
10108 }
10109
10110 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10111 {
10112   int i;
10113
10114   for (i = 0; i < scores->num_entries; i++)
10115     scores->entry[i].score = getFile32BitBE(file);
10116
10117   chunk_size = scores->num_entries * 4;
10118
10119   return chunk_size;
10120 }
10121
10122 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10123 {
10124   int i;
10125
10126   for (i = 0; i < scores->num_entries; i++)
10127     scores->entry[i].time = getFile32BitBE(file);
10128
10129   chunk_size = scores->num_entries * 4;
10130
10131   return chunk_size;
10132 }
10133
10134 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10135 {
10136   int i, j;
10137
10138   for (i = 0; i < scores->num_entries; i++)
10139   {
10140     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10141       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10142
10143     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10144   }
10145
10146   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10147
10148   return chunk_size;
10149 }
10150
10151 void LoadScore(int nr)
10152 {
10153   char *filename = getScoreFilename(nr);
10154   char cookie[MAX_LINE_LEN];
10155   char chunk_name[CHUNK_ID_LEN + 1];
10156   int chunk_size;
10157   boolean old_score_file_format = FALSE;
10158   File *file;
10159
10160   // always start with reliable default values
10161   setScoreInfoToDefaults();
10162
10163   if (!(file = openFile(filename, MODE_READ)))
10164     return;
10165
10166   getFileChunkBE(file, chunk_name, NULL);
10167   if (strEqual(chunk_name, "RND1"))
10168   {
10169     getFile32BitBE(file);               // not used
10170
10171     getFileChunkBE(file, chunk_name, NULL);
10172     if (!strEqual(chunk_name, "SCOR"))
10173     {
10174       Warn("unknown format of score file '%s'", filename);
10175
10176       closeFile(file);
10177
10178       return;
10179     }
10180   }
10181   else  // check for old file format with cookie string
10182   {
10183     strcpy(cookie, chunk_name);
10184     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10185       cookie[4] = '\0';
10186     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10187       cookie[strlen(cookie) - 1] = '\0';
10188
10189     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10190     {
10191       Warn("unknown format of score file '%s'", filename);
10192
10193       closeFile(file);
10194
10195       return;
10196     }
10197
10198     old_score_file_format = TRUE;
10199   }
10200
10201   if (old_score_file_format)
10202   {
10203     // score files from versions before 4.2.4.0 without chunk structure
10204     LoadScore_OLD(nr);
10205
10206     // convert score to time, if possible (mainly for Supaplex levels)
10207     ConvertScore_OLD();
10208   }
10209   else
10210   {
10211     static struct
10212     {
10213       char *name;
10214       int size;
10215       int (*loader)(File *, int, struct ScoreInfo *);
10216     }
10217     chunk_info[] =
10218     {
10219       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10220       { "INFO", -1,                     LoadScore_INFO },
10221       { "NAME", -1,                     LoadScore_NAME },
10222       { "SCOR", -1,                     LoadScore_SCOR },
10223       { "SC4R", -1,                     LoadScore_SC4R },
10224       { "TIME", -1,                     LoadScore_TIME },
10225       { "TAPE", -1,                     LoadScore_TAPE },
10226
10227       {  NULL,  0,                      NULL }
10228     };
10229
10230     while (getFileChunkBE(file, chunk_name, &chunk_size))
10231     {
10232       int i = 0;
10233
10234       while (chunk_info[i].name != NULL &&
10235              !strEqual(chunk_name, chunk_info[i].name))
10236         i++;
10237
10238       if (chunk_info[i].name == NULL)
10239       {
10240         Warn("unknown chunk '%s' in score file '%s'",
10241               chunk_name, filename);
10242
10243         ReadUnusedBytesFromFile(file, chunk_size);
10244       }
10245       else if (chunk_info[i].size != -1 &&
10246                chunk_info[i].size != chunk_size)
10247       {
10248         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10249               chunk_size, chunk_name, filename);
10250
10251         ReadUnusedBytesFromFile(file, chunk_size);
10252       }
10253       else
10254       {
10255         // call function to load this score chunk
10256         int chunk_size_expected =
10257           (chunk_info[i].loader)(file, chunk_size, &scores);
10258
10259         // the size of some chunks cannot be checked before reading other
10260         // chunks first (like "HEAD" and "BODY") that contain some header
10261         // information, so check them here
10262         if (chunk_size_expected != chunk_size)
10263         {
10264           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10265                 chunk_size, chunk_name, filename);
10266         }
10267       }
10268     }
10269   }
10270
10271   closeFile(file);
10272 }
10273
10274 #if ENABLE_HISTORIC_CHUNKS
10275 void SaveScore_OLD(int nr)
10276 {
10277   int i;
10278   char *filename = getScoreFilename(nr);
10279   FILE *file;
10280
10281   // used instead of "leveldir_current->subdir" (for network games)
10282   InitScoreDirectory(levelset.identifier);
10283
10284   if (!(file = fopen(filename, MODE_WRITE)))
10285   {
10286     Warn("cannot save score for level %d", nr);
10287
10288     return;
10289   }
10290
10291   fprintf(file, "%s\n\n", SCORE_COOKIE);
10292
10293   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10294     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10295
10296   fclose(file);
10297
10298   SetFilePermissions(filename, PERMS_PRIVATE);
10299 }
10300 #endif
10301
10302 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10303 {
10304   putFileVersion(file, scores->file_version);
10305   putFileVersion(file, scores->game_version);
10306 }
10307
10308 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10309 {
10310   int level_identifier_size = strlen(scores->level_identifier) + 1;
10311   int i;
10312
10313   putFile16BitBE(file, level_identifier_size);
10314
10315   for (i = 0; i < level_identifier_size; i++)
10316     putFile8Bit(file, scores->level_identifier[i]);
10317
10318   putFile16BitBE(file, scores->level_nr);
10319   putFile16BitBE(file, scores->num_entries);
10320 }
10321
10322 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10323 {
10324   int i, j;
10325
10326   for (i = 0; i < scores->num_entries; i++)
10327   {
10328     int name_size = strlen(scores->entry[i].name);
10329
10330     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10331       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10332   }
10333 }
10334
10335 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10336 {
10337   int i;
10338
10339   for (i = 0; i < scores->num_entries; i++)
10340     putFile16BitBE(file, scores->entry[i].score);
10341 }
10342
10343 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10344 {
10345   int i;
10346
10347   for (i = 0; i < scores->num_entries; i++)
10348     putFile32BitBE(file, scores->entry[i].score);
10349 }
10350
10351 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10352 {
10353   int i;
10354
10355   for (i = 0; i < scores->num_entries; i++)
10356     putFile32BitBE(file, scores->entry[i].time);
10357 }
10358
10359 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10360 {
10361   int i, j;
10362
10363   for (i = 0; i < scores->num_entries; i++)
10364   {
10365     int size = strlen(scores->entry[i].tape_basename);
10366
10367     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10368       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10369   }
10370 }
10371
10372 static void SaveScoreToFilename(char *filename)
10373 {
10374   FILE *file;
10375   int info_chunk_size;
10376   int name_chunk_size;
10377   int scor_chunk_size;
10378   int sc4r_chunk_size;
10379   int time_chunk_size;
10380   int tape_chunk_size;
10381   boolean has_large_score_values;
10382   int i;
10383
10384   if (!(file = fopen(filename, MODE_WRITE)))
10385   {
10386     Warn("cannot save score file '%s'", filename);
10387
10388     return;
10389   }
10390
10391   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10392   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10393   scor_chunk_size = scores.num_entries * 2;
10394   sc4r_chunk_size = scores.num_entries * 4;
10395   time_chunk_size = scores.num_entries * 4;
10396   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10397
10398   has_large_score_values = FALSE;
10399   for (i = 0; i < scores.num_entries; i++)
10400     if (scores.entry[i].score > 0xffff)
10401       has_large_score_values = TRUE;
10402
10403   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10404   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10405
10406   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10407   SaveScore_VERS(file, &scores);
10408
10409   putFileChunkBE(file, "INFO", info_chunk_size);
10410   SaveScore_INFO(file, &scores);
10411
10412   putFileChunkBE(file, "NAME", name_chunk_size);
10413   SaveScore_NAME(file, &scores);
10414
10415   if (has_large_score_values)
10416   {
10417     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10418     SaveScore_SC4R(file, &scores);
10419   }
10420   else
10421   {
10422     putFileChunkBE(file, "SCOR", scor_chunk_size);
10423     SaveScore_SCOR(file, &scores);
10424   }
10425
10426   putFileChunkBE(file, "TIME", time_chunk_size);
10427   SaveScore_TIME(file, &scores);
10428
10429   putFileChunkBE(file, "TAPE", tape_chunk_size);
10430   SaveScore_TAPE(file, &scores);
10431
10432   fclose(file);
10433
10434   SetFilePermissions(filename, PERMS_PRIVATE);
10435 }
10436
10437 void SaveScore(int nr)
10438 {
10439   char *filename = getScoreFilename(nr);
10440   int i;
10441
10442   // used instead of "leveldir_current->subdir" (for network games)
10443   InitScoreDirectory(levelset.identifier);
10444
10445   scores.file_version = FILE_VERSION_ACTUAL;
10446   scores.game_version = GAME_VERSION_ACTUAL;
10447
10448   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10449   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10450   scores.level_nr = level_nr;
10451
10452   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10453     if (scores.entry[i].score == 0 &&
10454         scores.entry[i].time == 0 &&
10455         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10456       break;
10457
10458   scores.num_entries = i;
10459
10460   if (scores.num_entries == 0)
10461     return;
10462
10463   SaveScoreToFilename(filename);
10464 }
10465
10466 static void LoadServerScoreFromCache(int nr)
10467 {
10468   struct ScoreEntry score_entry;
10469   struct
10470   {
10471     void *value;
10472     boolean is_string;
10473     int string_size;
10474   }
10475   score_mapping[] =
10476   {
10477     { &score_entry.score,               FALSE,  0                       },
10478     { &score_entry.time,                FALSE,  0                       },
10479     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10480     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10481     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10482     { &score_entry.id,                  FALSE,  0                       },
10483     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10484     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10485     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10486     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10487
10488     { NULL,                             FALSE,  0                       }
10489   };
10490   char *filename = getScoreCacheFilename(nr);
10491   SetupFileHash *score_hash = loadSetupFileHash(filename);
10492   int i, j;
10493
10494   server_scores.num_entries = 0;
10495
10496   if (score_hash == NULL)
10497     return;
10498
10499   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10500   {
10501     score_entry = server_scores.entry[i];
10502
10503     for (j = 0; score_mapping[j].value != NULL; j++)
10504     {
10505       char token[10];
10506
10507       sprintf(token, "%02d.%d", i, j);
10508
10509       char *value = getHashEntry(score_hash, token);
10510
10511       if (value == NULL)
10512         continue;
10513
10514       if (score_mapping[j].is_string)
10515       {
10516         char *score_value = (char *)score_mapping[j].value;
10517         int value_size = score_mapping[j].string_size;
10518
10519         strncpy(score_value, value, value_size);
10520         score_value[value_size] = '\0';
10521       }
10522       else
10523       {
10524         int *score_value = (int *)score_mapping[j].value;
10525
10526         *score_value = atoi(value);
10527       }
10528
10529       server_scores.num_entries = i + 1;
10530     }
10531
10532     server_scores.entry[i] = score_entry;
10533   }
10534
10535   freeSetupFileHash(score_hash);
10536 }
10537
10538 void LoadServerScore(int nr, boolean download_score)
10539 {
10540   if (!setup.use_api_server)
10541     return;
10542
10543   // always start with reliable default values
10544   setServerScoreInfoToDefaults();
10545
10546   // 1st step: load server scores from cache file (which may not exist)
10547   // (this should prevent reading it while the thread is writing to it)
10548   LoadServerScoreFromCache(nr);
10549
10550   if (download_score && runtime.use_api_server)
10551   {
10552     // 2nd step: download server scores from score server to cache file
10553     // (as thread, as it might time out if the server is not reachable)
10554     ApiGetScoreAsThread(nr);
10555   }
10556 }
10557
10558 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10559 {
10560   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10561
10562   // if score tape not uploaded, ask for uploading missing tapes later
10563   if (!setup.has_remaining_tapes)
10564     setup.ask_for_remaining_tapes = TRUE;
10565
10566   setup.provide_uploading_tapes = TRUE;
10567   setup.has_remaining_tapes = TRUE;
10568
10569   SaveSetup_ServerSetup();
10570 }
10571
10572 void SaveServerScore(int nr, boolean tape_saved)
10573 {
10574   if (!runtime.use_api_server)
10575   {
10576     PrepareScoreTapesForUpload(leveldir_current->subdir);
10577
10578     return;
10579   }
10580
10581   ApiAddScoreAsThread(nr, tape_saved, NULL);
10582 }
10583
10584 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10585                              char *score_tape_filename)
10586 {
10587   if (!runtime.use_api_server)
10588     return;
10589
10590   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10591 }
10592
10593 void LoadLocalAndServerScore(int nr, boolean download_score)
10594 {
10595   int last_added_local = scores.last_added_local;
10596   boolean force_last_added = scores.force_last_added;
10597
10598   // needed if only showing server scores
10599   setScoreInfoToDefaults();
10600
10601   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10602     LoadScore(nr);
10603
10604   // restore last added local score entry (before merging server scores)
10605   scores.last_added = scores.last_added_local = last_added_local;
10606
10607   if (setup.use_api_server &&
10608       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10609   {
10610     // load server scores from cache file and trigger update from server
10611     LoadServerScore(nr, download_score);
10612
10613     // merge local scores with scores from server
10614     MergeServerScore();
10615   }
10616
10617   if (force_last_added)
10618     scores.force_last_added = force_last_added;
10619 }
10620
10621
10622 // ============================================================================
10623 // setup file functions
10624 // ============================================================================
10625
10626 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10627
10628
10629 static struct TokenInfo global_setup_tokens[] =
10630 {
10631   {
10632     TYPE_STRING,
10633     &setup.player_name,                         "player_name"
10634   },
10635   {
10636     TYPE_SWITCH,
10637     &setup.multiple_users,                      "multiple_users"
10638   },
10639   {
10640     TYPE_SWITCH,
10641     &setup.sound,                               "sound"
10642   },
10643   {
10644     TYPE_SWITCH,
10645     &setup.sound_loops,                         "repeating_sound_loops"
10646   },
10647   {
10648     TYPE_SWITCH,
10649     &setup.sound_music,                         "background_music"
10650   },
10651   {
10652     TYPE_SWITCH,
10653     &setup.sound_simple,                        "simple_sound_effects"
10654   },
10655   {
10656     TYPE_SWITCH,
10657     &setup.toons,                               "toons"
10658   },
10659   {
10660     TYPE_SWITCH,
10661     &setup.global_animations,                   "global_animations"
10662   },
10663   {
10664     TYPE_SWITCH,
10665     &setup.scroll_delay,                        "scroll_delay"
10666   },
10667   {
10668     TYPE_SWITCH,
10669     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10670   },
10671   {
10672     TYPE_INTEGER,
10673     &setup.scroll_delay_value,                  "scroll_delay_value"
10674   },
10675   {
10676     TYPE_STRING,
10677     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10678   },
10679   {
10680     TYPE_INTEGER,
10681     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10682   },
10683   {
10684     TYPE_SWITCH,
10685     &setup.fade_screens,                        "fade_screens"
10686   },
10687   {
10688     TYPE_SWITCH,
10689     &setup.autorecord,                          "automatic_tape_recording"
10690   },
10691   {
10692     TYPE_SWITCH,
10693     &setup.autorecord_after_replay,             "autorecord_after_replay"
10694   },
10695   {
10696     TYPE_SWITCH,
10697     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10698   },
10699   {
10700     TYPE_SWITCH,
10701     &setup.show_titlescreen,                    "show_titlescreen"
10702   },
10703   {
10704     TYPE_SWITCH,
10705     &setup.quick_doors,                         "quick_doors"
10706   },
10707   {
10708     TYPE_SWITCH,
10709     &setup.team_mode,                           "team_mode"
10710   },
10711   {
10712     TYPE_SWITCH,
10713     &setup.handicap,                            "handicap"
10714   },
10715   {
10716     TYPE_SWITCH,
10717     &setup.skip_levels,                         "skip_levels"
10718   },
10719   {
10720     TYPE_SWITCH_3_STATES,
10721     &setup.allow_skipping_levels,               "allow_skipping_levels"
10722   },
10723   {
10724     TYPE_SWITCH,
10725     &setup.increment_levels,                    "increment_levels"
10726   },
10727   {
10728     TYPE_SWITCH,
10729     &setup.auto_play_next_level,                "auto_play_next_level"
10730   },
10731   {
10732     TYPE_SWITCH,
10733     &setup.count_score_after_game,              "count_score_after_game"
10734   },
10735   {
10736     TYPE_SWITCH,
10737     &setup.show_scores_after_game,              "show_scores_after_game"
10738   },
10739   {
10740     TYPE_SWITCH,
10741     &setup.time_limit,                          "time_limit"
10742   },
10743   {
10744     TYPE_SWITCH,
10745     &setup.fullscreen,                          "fullscreen"
10746   },
10747   {
10748     TYPE_INTEGER,
10749     &setup.window_scaling_percent,              "window_scaling_percent"
10750   },
10751   {
10752     TYPE_STRING,
10753     &setup.window_scaling_quality,              "window_scaling_quality"
10754   },
10755   {
10756     TYPE_STRING,
10757     &setup.screen_rendering_mode,               "screen_rendering_mode"
10758   },
10759   {
10760     TYPE_STRING,
10761     &setup.vsync_mode,                          "vsync_mode"
10762   },
10763   {
10764     TYPE_SWITCH,
10765     &setup.ask_on_escape,                       "ask_on_escape"
10766   },
10767   {
10768     TYPE_SWITCH,
10769     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10770   },
10771   {
10772     TYPE_SWITCH,
10773     &setup.ask_on_game_over,                    "ask_on_game_over"
10774   },
10775   {
10776     TYPE_SWITCH,
10777     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10778   },
10779   {
10780     TYPE_SWITCH,
10781     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10782   },
10783   {
10784     TYPE_SWITCH,
10785     &setup.quick_switch,                        "quick_player_switch"
10786   },
10787   {
10788     TYPE_SWITCH,
10789     &setup.input_on_focus,                      "input_on_focus"
10790   },
10791   {
10792     TYPE_SWITCH,
10793     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10794   },
10795   {
10796     TYPE_SWITCH,
10797     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10798   },
10799   {
10800     TYPE_SWITCH,
10801     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10802   },
10803   {
10804     TYPE_SWITCH,
10805     &setup.game_speed_extended,                 "game_speed_extended"
10806   },
10807   {
10808     TYPE_INTEGER,
10809     &setup.game_frame_delay,                    "game_frame_delay"
10810   },
10811   {
10812     TYPE_INTEGER,
10813     &setup.default_game_engine_type,            "default_game_engine_type"
10814   },
10815   {
10816     TYPE_SWITCH,
10817     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10818   },
10819   {
10820     TYPE_SWITCH,
10821     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10822   },
10823   {
10824     TYPE_SWITCH,
10825     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10826   },
10827   {
10828     TYPE_SWITCH,
10829     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10830   },
10831   {
10832     TYPE_SWITCH_3_STATES,
10833     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10834   },
10835   {
10836     TYPE_SWITCH_3_STATES,
10837     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10838   },
10839   {
10840     TYPE_SWITCH_3_STATES,
10841     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10842   },
10843   {
10844     TYPE_SWITCH_3_STATES,
10845     &setup.bd_skip_falling_sounds,              "bd_skip_falling_sounds"
10846   },
10847   {
10848     TYPE_INTEGER,
10849     &setup.bd_palette_c64,                      "bd_palette_c64"
10850   },
10851   {
10852     TYPE_INTEGER,
10853     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10854   },
10855   {
10856     TYPE_INTEGER,
10857     &setup.bd_palette_atari,                    "bd_palette_atari"
10858   },
10859   {
10860     TYPE_INTEGER,
10861     &setup.bd_default_color_type,               "bd_default_color_type"
10862   },
10863   {
10864     TYPE_SWITCH,
10865     &setup.bd_random_colors,                    "bd_random_colors"
10866   },
10867   {
10868     TYPE_SWITCH,
10869     &setup.sp_show_border_elements,             "sp_show_border_elements"
10870   },
10871   {
10872     TYPE_SWITCH,
10873     &setup.small_game_graphics,                 "small_game_graphics"
10874   },
10875   {
10876     TYPE_SWITCH,
10877     &setup.show_load_save_buttons,              "show_load_save_buttons"
10878   },
10879   {
10880     TYPE_SWITCH,
10881     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10882   },
10883   {
10884     TYPE_STRING,
10885     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10886   },
10887   {
10888     TYPE_STRING,
10889     &setup.graphics_set,                        "graphics_set"
10890   },
10891   {
10892     TYPE_STRING,
10893     &setup.sounds_set,                          "sounds_set"
10894   },
10895   {
10896     TYPE_STRING,
10897     &setup.music_set,                           "music_set"
10898   },
10899   {
10900     TYPE_SWITCH_3_STATES,
10901     &setup.override_level_graphics,             "override_level_graphics"
10902   },
10903   {
10904     TYPE_SWITCH_3_STATES,
10905     &setup.override_level_sounds,               "override_level_sounds"
10906   },
10907   {
10908     TYPE_SWITCH_3_STATES,
10909     &setup.override_level_music,                "override_level_music"
10910   },
10911   {
10912     TYPE_INTEGER,
10913     &setup.volume_simple,                       "volume_simple"
10914   },
10915   {
10916     TYPE_INTEGER,
10917     &setup.volume_loops,                        "volume_loops"
10918   },
10919   {
10920     TYPE_INTEGER,
10921     &setup.volume_music,                        "volume_music"
10922   },
10923   {
10924     TYPE_SWITCH,
10925     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10926   },
10927   {
10928     TYPE_SWITCH,
10929     &setup.network_mode,                        "network_mode"
10930   },
10931   {
10932     TYPE_PLAYER,
10933     &setup.network_player_nr,                   "network_player"
10934   },
10935   {
10936     TYPE_STRING,
10937     &setup.network_server_hostname,             "network_server_hostname"
10938   },
10939   {
10940     TYPE_STRING,
10941     &setup.touch.control_type,                  "touch.control_type"
10942   },
10943   {
10944     TYPE_INTEGER,
10945     &setup.touch.move_distance,                 "touch.move_distance"
10946   },
10947   {
10948     TYPE_INTEGER,
10949     &setup.touch.drop_distance,                 "touch.drop_distance"
10950   },
10951   {
10952     TYPE_INTEGER,
10953     &setup.touch.transparency,                  "touch.transparency"
10954   },
10955   {
10956     TYPE_INTEGER,
10957     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10958   },
10959   {
10960     TYPE_INTEGER,
10961     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10962   },
10963   {
10964     TYPE_INTEGER,
10965     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10966   },
10967   {
10968     TYPE_INTEGER,
10969     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10970   },
10971   {
10972     TYPE_INTEGER,
10973     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10974   },
10975   {
10976     TYPE_INTEGER,
10977     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10978   },
10979   {
10980     TYPE_SWITCH,
10981     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10982   },
10983 };
10984
10985 static struct TokenInfo auto_setup_tokens[] =
10986 {
10987   {
10988     TYPE_INTEGER,
10989     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10990   },
10991 };
10992
10993 static struct TokenInfo server_setup_tokens[] =
10994 {
10995   {
10996     TYPE_STRING,
10997     &setup.player_uuid,                         "player_uuid"
10998   },
10999   {
11000     TYPE_INTEGER,
11001     &setup.player_version,                      "player_version"
11002   },
11003   {
11004     TYPE_SWITCH,
11005     &setup.use_api_server,          TEST_PREFIX "use_api_server"
11006   },
11007   {
11008     TYPE_STRING,
11009     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
11010   },
11011   {
11012     TYPE_STRING,
11013     &setup.api_server_password,     TEST_PREFIX "api_server_password"
11014   },
11015   {
11016     TYPE_SWITCH,
11017     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11018   },
11019   {
11020     TYPE_SWITCH,
11021     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11022   },
11023   {
11024     TYPE_SWITCH,
11025     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11026   },
11027   {
11028     TYPE_SWITCH,
11029     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11030   },
11031   {
11032     TYPE_SWITCH,
11033     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
11034   },
11035 };
11036
11037 static struct TokenInfo editor_setup_tokens[] =
11038 {
11039   {
11040     TYPE_SWITCH,
11041     &setup.editor.el_classic,                   "editor.el_classic"
11042   },
11043   {
11044     TYPE_SWITCH,
11045     &setup.editor.el_custom,                    "editor.el_custom"
11046   },
11047   {
11048     TYPE_SWITCH,
11049     &setup.editor.el_user_defined,              "editor.el_user_defined"
11050   },
11051   {
11052     TYPE_SWITCH,
11053     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11054   },
11055   {
11056     TYPE_SWITCH,
11057     &setup.editor.el_headlines,                 "editor.el_headlines"
11058   },
11059   {
11060     TYPE_SWITCH,
11061     &setup.editor.show_element_token,           "editor.show_element_token"
11062   },
11063   {
11064     TYPE_SWITCH,
11065     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11066   },
11067 };
11068
11069 static struct TokenInfo editor_cascade_setup_tokens[] =
11070 {
11071   {
11072     TYPE_SWITCH,
11073     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11074   },
11075   {
11076     TYPE_SWITCH,
11077     &setup.editor_cascade.el_bdx,               "editor.cascade.el_bdx"
11078   },
11079   {
11080     TYPE_SWITCH,
11081     &setup.editor_cascade.el_bdx_effects,       "editor.cascade.el_bdx_effects"
11082   },
11083   {
11084     TYPE_SWITCH,
11085     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11086   },
11087   {
11088     TYPE_SWITCH,
11089     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11090   },
11091   {
11092     TYPE_SWITCH,
11093     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11094   },
11095   {
11096     TYPE_SWITCH,
11097     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11098   },
11099   {
11100     TYPE_SWITCH,
11101     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11102   },
11103   {
11104     TYPE_SWITCH,
11105     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11106   },
11107   {
11108     TYPE_SWITCH,
11109     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11110   },
11111   {
11112     TYPE_SWITCH,
11113     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11114   },
11115   {
11116     TYPE_SWITCH,
11117     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11118   },
11119   {
11120     TYPE_SWITCH,
11121     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11122   },
11123   {
11124     TYPE_SWITCH,
11125     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11126   },
11127   {
11128     TYPE_SWITCH,
11129     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11130   },
11131   {
11132     TYPE_SWITCH,
11133     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11134   },
11135   {
11136     TYPE_SWITCH,
11137     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11138   },
11139   {
11140     TYPE_SWITCH,
11141     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11142   },
11143   {
11144     TYPE_SWITCH,
11145     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11146   },
11147   {
11148     TYPE_SWITCH,
11149     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11150   },
11151 };
11152
11153 static struct TokenInfo shortcut_setup_tokens[] =
11154 {
11155   {
11156     TYPE_KEY_X11,
11157     &setup.shortcut.save_game,                  "shortcut.save_game"
11158   },
11159   {
11160     TYPE_KEY_X11,
11161     &setup.shortcut.load_game,                  "shortcut.load_game"
11162   },
11163   {
11164     TYPE_KEY_X11,
11165     &setup.shortcut.restart_game,               "shortcut.restart_game"
11166   },
11167   {
11168     TYPE_KEY_X11,
11169     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11170   },
11171   {
11172     TYPE_KEY_X11,
11173     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11174   },
11175   {
11176     TYPE_KEY_X11,
11177     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11178   },
11179   {
11180     TYPE_KEY_X11,
11181     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11206   },
11207   {
11208     TYPE_KEY_X11,
11209     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11210   },
11211   {
11212     TYPE_KEY_X11,
11213     &setup.shortcut.tape_record,                "shortcut.tape_record"
11214   },
11215   {
11216     TYPE_KEY_X11,
11217     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11218   },
11219   {
11220     TYPE_KEY_X11,
11221     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11222   },
11223   {
11224     TYPE_KEY_X11,
11225     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11226   },
11227   {
11228     TYPE_KEY_X11,
11229     &setup.shortcut.sound_music,                "shortcut.sound_music"
11230   },
11231   {
11232     TYPE_KEY_X11,
11233     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11234   },
11235   {
11236     TYPE_KEY_X11,
11237     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11238   },
11239   {
11240     TYPE_KEY_X11,
11241     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11242   },
11243   {
11244     TYPE_KEY_X11,
11245     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11246   },
11247   {
11248     TYPE_KEY_X11,
11249     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11250   },
11251   {
11252     TYPE_KEY_X11,
11253     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11254   },
11255 };
11256
11257 static struct SetupInputInfo setup_input;
11258 static struct TokenInfo player_setup_tokens[] =
11259 {
11260   {
11261     TYPE_BOOLEAN,
11262     &setup_input.use_joystick,                  ".use_joystick"
11263   },
11264   {
11265     TYPE_STRING,
11266     &setup_input.joy.device_name,               ".joy.device_name"
11267   },
11268   {
11269     TYPE_INTEGER,
11270     &setup_input.joy.xleft,                     ".joy.xleft"
11271   },
11272   {
11273     TYPE_INTEGER,
11274     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11275   },
11276   {
11277     TYPE_INTEGER,
11278     &setup_input.joy.xright,                    ".joy.xright"
11279   },
11280   {
11281     TYPE_INTEGER,
11282     &setup_input.joy.yupper,                    ".joy.yupper"
11283   },
11284   {
11285     TYPE_INTEGER,
11286     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11287   },
11288   {
11289     TYPE_INTEGER,
11290     &setup_input.joy.ylower,                    ".joy.ylower"
11291   },
11292   {
11293     TYPE_INTEGER,
11294     &setup_input.joy.snap,                      ".joy.snap_field"
11295   },
11296   {
11297     TYPE_INTEGER,
11298     &setup_input.joy.drop,                      ".joy.place_bomb"
11299   },
11300   {
11301     TYPE_KEY_X11,
11302     &setup_input.key.left,                      ".key.move_left"
11303   },
11304   {
11305     TYPE_KEY_X11,
11306     &setup_input.key.right,                     ".key.move_right"
11307   },
11308   {
11309     TYPE_KEY_X11,
11310     &setup_input.key.up,                        ".key.move_up"
11311   },
11312   {
11313     TYPE_KEY_X11,
11314     &setup_input.key.down,                      ".key.move_down"
11315   },
11316   {
11317     TYPE_KEY_X11,
11318     &setup_input.key.snap,                      ".key.snap_field"
11319   },
11320   {
11321     TYPE_KEY_X11,
11322     &setup_input.key.drop,                      ".key.place_bomb"
11323   },
11324 };
11325
11326 static struct TokenInfo system_setup_tokens[] =
11327 {
11328   {
11329     TYPE_STRING,
11330     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11331   },
11332   {
11333     TYPE_STRING,
11334     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11335   },
11336   {
11337     TYPE_STRING,
11338     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11339   },
11340   {
11341     TYPE_INTEGER,
11342     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11343   },
11344 };
11345
11346 static struct TokenInfo internal_setup_tokens[] =
11347 {
11348   {
11349     TYPE_STRING,
11350     &setup.internal.program_title,              "program_title"
11351   },
11352   {
11353     TYPE_STRING,
11354     &setup.internal.program_version,            "program_version"
11355   },
11356   {
11357     TYPE_STRING,
11358     &setup.internal.program_author,             "program_author"
11359   },
11360   {
11361     TYPE_STRING,
11362     &setup.internal.program_email,              "program_email"
11363   },
11364   {
11365     TYPE_STRING,
11366     &setup.internal.program_website,            "program_website"
11367   },
11368   {
11369     TYPE_STRING,
11370     &setup.internal.program_copyright,          "program_copyright"
11371   },
11372   {
11373     TYPE_STRING,
11374     &setup.internal.program_company,            "program_company"
11375   },
11376   {
11377     TYPE_STRING,
11378     &setup.internal.program_icon_file,          "program_icon_file"
11379   },
11380   {
11381     TYPE_STRING,
11382     &setup.internal.default_graphics_set,       "default_graphics_set"
11383   },
11384   {
11385     TYPE_STRING,
11386     &setup.internal.default_sounds_set,         "default_sounds_set"
11387   },
11388   {
11389     TYPE_STRING,
11390     &setup.internal.default_music_set,          "default_music_set"
11391   },
11392   {
11393     TYPE_STRING,
11394     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11395   },
11396   {
11397     TYPE_STRING,
11398     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11399   },
11400   {
11401     TYPE_STRING,
11402     &setup.internal.fallback_music_file,        "fallback_music_file"
11403   },
11404   {
11405     TYPE_STRING,
11406     &setup.internal.default_level_series,       "default_level_series"
11407   },
11408   {
11409     TYPE_INTEGER,
11410     &setup.internal.default_window_width,       "default_window_width"
11411   },
11412   {
11413     TYPE_INTEGER,
11414     &setup.internal.default_window_height,      "default_window_height"
11415   },
11416   {
11417     TYPE_BOOLEAN,
11418     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11419   },
11420   {
11421     TYPE_BOOLEAN,
11422     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11423   },
11424   {
11425     TYPE_BOOLEAN,
11426     &setup.internal.create_user_levelset,       "create_user_levelset"
11427   },
11428   {
11429     TYPE_BOOLEAN,
11430     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11431   },
11432   {
11433     TYPE_BOOLEAN,
11434     &setup.internal.menu_game,                  "menu_game"
11435   },
11436   {
11437     TYPE_BOOLEAN,
11438     &setup.internal.menu_engines,               "menu_engines"
11439   },
11440   {
11441     TYPE_BOOLEAN,
11442     &setup.internal.menu_editor,                "menu_editor"
11443   },
11444   {
11445     TYPE_BOOLEAN,
11446     &setup.internal.menu_graphics,              "menu_graphics"
11447   },
11448   {
11449     TYPE_BOOLEAN,
11450     &setup.internal.menu_sound,                 "menu_sound"
11451   },
11452   {
11453     TYPE_BOOLEAN,
11454     &setup.internal.menu_artwork,               "menu_artwork"
11455   },
11456   {
11457     TYPE_BOOLEAN,
11458     &setup.internal.menu_input,                 "menu_input"
11459   },
11460   {
11461     TYPE_BOOLEAN,
11462     &setup.internal.menu_touch,                 "menu_touch"
11463   },
11464   {
11465     TYPE_BOOLEAN,
11466     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11467   },
11468   {
11469     TYPE_BOOLEAN,
11470     &setup.internal.menu_exit,                  "menu_exit"
11471   },
11472   {
11473     TYPE_BOOLEAN,
11474     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11475   },
11476   {
11477     TYPE_BOOLEAN,
11478     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11479   },
11480   {
11481     TYPE_BOOLEAN,
11482     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11483   },
11484   {
11485     TYPE_BOOLEAN,
11486     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11487   },
11488   {
11489     TYPE_BOOLEAN,
11490     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11491   },
11492   {
11493     TYPE_BOOLEAN,
11494     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11495   },
11496   {
11497     TYPE_BOOLEAN,
11498     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11499   },
11500   {
11501     TYPE_BOOLEAN,
11502     &setup.internal.info_title,                 "info_title"
11503   },
11504   {
11505     TYPE_BOOLEAN,
11506     &setup.internal.info_elements,              "info_elements"
11507   },
11508   {
11509     TYPE_BOOLEAN,
11510     &setup.internal.info_music,                 "info_music"
11511   },
11512   {
11513     TYPE_BOOLEAN,
11514     &setup.internal.info_credits,               "info_credits"
11515   },
11516   {
11517     TYPE_BOOLEAN,
11518     &setup.internal.info_program,               "info_program"
11519   },
11520   {
11521     TYPE_BOOLEAN,
11522     &setup.internal.info_version,               "info_version"
11523   },
11524   {
11525     TYPE_BOOLEAN,
11526     &setup.internal.info_levelset,              "info_levelset"
11527   },
11528   {
11529     TYPE_BOOLEAN,
11530     &setup.internal.info_exit,                  "info_exit"
11531   },
11532 };
11533
11534 static struct TokenInfo debug_setup_tokens[] =
11535 {
11536   {
11537     TYPE_INTEGER,
11538     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11539   },
11540   {
11541     TYPE_INTEGER,
11542     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11543   },
11544   {
11545     TYPE_INTEGER,
11546     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11547   },
11548   {
11549     TYPE_INTEGER,
11550     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11551   },
11552   {
11553     TYPE_INTEGER,
11554     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11555   },
11556   {
11557     TYPE_INTEGER,
11558     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11559   },
11560   {
11561     TYPE_INTEGER,
11562     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11563   },
11564   {
11565     TYPE_INTEGER,
11566     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11567   },
11568   {
11569     TYPE_INTEGER,
11570     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11571   },
11572   {
11573     TYPE_INTEGER,
11574     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11575   },
11576   {
11577     TYPE_KEY_X11,
11578     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11579   },
11580   {
11581     TYPE_KEY_X11,
11582     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11583   },
11584   {
11585     TYPE_KEY_X11,
11586     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11587   },
11588   {
11589     TYPE_KEY_X11,
11590     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11591   },
11592   {
11593     TYPE_KEY_X11,
11594     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11595   },
11596   {
11597     TYPE_KEY_X11,
11598     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11599   },
11600   {
11601     TYPE_KEY_X11,
11602     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11603   },
11604   {
11605     TYPE_KEY_X11,
11606     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11607   },
11608   {
11609     TYPE_KEY_X11,
11610     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11611   },
11612   {
11613     TYPE_KEY_X11,
11614     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11615   },
11616   {
11617     TYPE_BOOLEAN,
11618     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11619   {
11620     TYPE_BOOLEAN,
11621     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11622   },
11623   {
11624     TYPE_BOOLEAN,
11625     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11626   },
11627   {
11628     TYPE_SWITCH_3_STATES,
11629     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11630   },
11631   {
11632     TYPE_INTEGER,
11633     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11634   },
11635 };
11636
11637 static struct TokenInfo options_setup_tokens[] =
11638 {
11639   {
11640     TYPE_BOOLEAN,
11641     &setup.options.verbose,                     "options.verbose"
11642   },
11643   {
11644     TYPE_BOOLEAN,
11645     &setup.options.debug,                       "options.debug"
11646   },
11647   {
11648     TYPE_STRING,
11649     &setup.options.debug_mode,                  "options.debug_mode"
11650   },
11651 };
11652
11653 static void setSetupInfoToDefaults(struct SetupInfo *si)
11654 {
11655   int i;
11656
11657   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11658
11659   si->multiple_users = TRUE;
11660
11661   si->sound = TRUE;
11662   si->sound_loops = TRUE;
11663   si->sound_music = TRUE;
11664   si->sound_simple = TRUE;
11665   si->toons = TRUE;
11666   si->global_animations = TRUE;
11667   si->scroll_delay = TRUE;
11668   si->forced_scroll_delay = FALSE;
11669   si->scroll_delay_value = STD_SCROLL_DELAY;
11670   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11671   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11672   si->fade_screens = TRUE;
11673   si->autorecord = TRUE;
11674   si->autorecord_after_replay = TRUE;
11675   si->auto_pause_on_start = FALSE;
11676   si->show_titlescreen = TRUE;
11677   si->quick_doors = FALSE;
11678   si->team_mode = FALSE;
11679   si->handicap = TRUE;
11680   si->skip_levels = TRUE;
11681   si->allow_skipping_levels = STATE_ASK;
11682   si->increment_levels = TRUE;
11683   si->auto_play_next_level = TRUE;
11684   si->count_score_after_game = TRUE;
11685   si->show_scores_after_game = TRUE;
11686   si->time_limit = TRUE;
11687   si->fullscreen = FALSE;
11688   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11689   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11690   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11691   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11692   si->ask_on_escape = TRUE;
11693   si->ask_on_escape_editor = TRUE;
11694   si->ask_on_game_over = TRUE;
11695   si->ask_on_quit_game = TRUE;
11696   si->ask_on_quit_program = TRUE;
11697   si->quick_switch = FALSE;
11698   si->input_on_focus = FALSE;
11699   si->prefer_aga_graphics = TRUE;
11700   si->prefer_lowpass_sounds = FALSE;
11701   si->prefer_extra_panel_items = TRUE;
11702   si->game_speed_extended = FALSE;
11703   si->game_frame_delay = GAME_FRAME_DELAY;
11704   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11705   si->bd_skip_uncovering = FALSE;
11706   si->bd_skip_hatching = FALSE;
11707   si->bd_scroll_delay = TRUE;
11708   si->bd_show_invisible_outbox = FALSE;
11709   si->bd_smooth_movements = STATE_TRUE;
11710   si->bd_pushing_graphics = STATE_TRUE;
11711   si->bd_up_down_graphics = STATE_TRUE;
11712   si->bd_skip_falling_sounds = STATE_TRUE;
11713   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11714   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11715   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11716   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11717   si->bd_random_colors = FALSE;
11718   si->sp_show_border_elements = FALSE;
11719   si->small_game_graphics = FALSE;
11720   si->show_load_save_buttons = FALSE;
11721   si->show_undo_redo_buttons = FALSE;
11722   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11723
11724   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11725   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11726   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11727
11728   si->override_level_graphics = STATE_FALSE;
11729   si->override_level_sounds = STATE_FALSE;
11730   si->override_level_music = STATE_FALSE;
11731
11732   si->volume_simple = 100;              // percent
11733   si->volume_loops = 100;               // percent
11734   si->volume_music = 100;               // percent
11735   si->audio_sample_rate_44100 = FALSE;
11736
11737   si->network_mode = FALSE;
11738   si->network_player_nr = 0;            // first player
11739   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11740
11741   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11742   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11743   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11744   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11745   si->touch.draw_outlined = TRUE;
11746   si->touch.draw_pressed = TRUE;
11747
11748   for (i = 0; i < 2; i++)
11749   {
11750     char *default_grid_button[6][2] =
11751     {
11752       { "      ", "  ^^  " },
11753       { "      ", "  ^^  " },
11754       { "      ", "<<  >>" },
11755       { "      ", "<<  >>" },
11756       { "111222", "  vv  " },
11757       { "111222", "  vv  " }
11758     };
11759     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11760     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11761     int min_xsize = MIN(6, grid_xsize);
11762     int min_ysize = MIN(6, grid_ysize);
11763     int startx = grid_xsize - min_xsize;
11764     int starty = grid_ysize - min_ysize;
11765     int x, y;
11766
11767     // virtual buttons grid can only be set to defaults if video is initialized
11768     // (this will be repeated if virtual buttons are not loaded from setup file)
11769     if (video.initialized)
11770     {
11771       si->touch.grid_xsize[i] = grid_xsize;
11772       si->touch.grid_ysize[i] = grid_ysize;
11773     }
11774     else
11775     {
11776       si->touch.grid_xsize[i] = -1;
11777       si->touch.grid_ysize[i] = -1;
11778     }
11779
11780     for (x = 0; x < MAX_GRID_XSIZE; x++)
11781       for (y = 0; y < MAX_GRID_YSIZE; y++)
11782         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11783
11784     for (x = 0; x < min_xsize; x++)
11785       for (y = 0; y < min_ysize; y++)
11786         si->touch.grid_button[i][x][starty + y] =
11787           default_grid_button[y][0][x];
11788
11789     for (x = 0; x < min_xsize; x++)
11790       for (y = 0; y < min_ysize; y++)
11791         si->touch.grid_button[i][startx + x][starty + y] =
11792           default_grid_button[y][1][x];
11793   }
11794
11795   si->touch.grid_initialized            = video.initialized;
11796
11797   si->touch.overlay_buttons             = FALSE;
11798
11799   si->editor.el_boulderdash             = TRUE;
11800   si->editor.el_boulderdash_native      = TRUE;
11801   si->editor.el_boulderdash_effects     = TRUE;
11802   si->editor.el_emerald_mine            = TRUE;
11803   si->editor.el_emerald_mine_club       = TRUE;
11804   si->editor.el_more                    = TRUE;
11805   si->editor.el_sokoban                 = TRUE;
11806   si->editor.el_supaplex                = TRUE;
11807   si->editor.el_diamond_caves           = TRUE;
11808   si->editor.el_dx_boulderdash          = TRUE;
11809
11810   si->editor.el_mirror_magic            = TRUE;
11811   si->editor.el_deflektor               = TRUE;
11812
11813   si->editor.el_chars                   = TRUE;
11814   si->editor.el_steel_chars             = TRUE;
11815
11816   si->editor.el_classic                 = TRUE;
11817   si->editor.el_custom                  = TRUE;
11818
11819   si->editor.el_user_defined            = FALSE;
11820   si->editor.el_dynamic                 = TRUE;
11821
11822   si->editor.el_headlines               = TRUE;
11823
11824   si->editor.show_element_token         = FALSE;
11825
11826   si->editor.show_read_only_warning     = TRUE;
11827
11828   si->editor.use_template_for_new_levels = TRUE;
11829
11830   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11831   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11832   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11833   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11834   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11835
11836   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11837   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11838   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11839   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11840   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11841
11842   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11843   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11844   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11845   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11846   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11847   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11848
11849   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11850   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11851   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11852
11853   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11854   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11855   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11856   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11857
11858   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11859   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11860
11861   for (i = 0; i < MAX_PLAYERS; i++)
11862   {
11863     si->input[i].use_joystick = FALSE;
11864     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11865     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11866     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11867     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11868     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11869     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11870     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11871     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11872     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11873     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11874     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11875     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11876     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11877     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11878     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11879   }
11880
11881   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11882   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11883   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11884   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11885
11886   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11887   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11888   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11889   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11890   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11891   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11892   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11893
11894   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11895
11896   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11897   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11898   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11899
11900   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11901   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11902   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11903
11904   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11905   si->internal.choose_from_top_leveldir = FALSE;
11906   si->internal.show_scaling_in_title = TRUE;
11907   si->internal.create_user_levelset = TRUE;
11908   si->internal.info_screens_from_main = FALSE;
11909
11910   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11911   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11912
11913   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11914   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11915   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11916   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11917   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11918   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11919   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11920   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11921   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11922   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11923
11924   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11925   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11926   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11927   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11928   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11929   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11930   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11931   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11932   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11933   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11934
11935   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11936   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11937
11938   si->debug.show_frames_per_second = FALSE;
11939
11940   si->debug.xsn_mode = STATE_AUTO;
11941   si->debug.xsn_percent = 0;
11942
11943   si->options.verbose = FALSE;
11944   si->options.debug = FALSE;
11945   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11946
11947 #if defined(PLATFORM_ANDROID)
11948   si->fullscreen = TRUE;
11949   si->touch.overlay_buttons = TRUE;
11950 #endif
11951
11952   setHideSetupEntry(&setup.debug.xsn_mode);
11953 }
11954
11955 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11956 {
11957   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11958 }
11959
11960 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11961 {
11962   si->player_uuid = NULL;       // (will be set later)
11963   si->player_version = 1;       // (will be set later)
11964
11965   si->use_api_server = TRUE;
11966   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11967   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11968   si->ask_for_uploading_tapes = TRUE;
11969   si->ask_for_remaining_tapes = FALSE;
11970   si->provide_uploading_tapes = TRUE;
11971   si->ask_for_using_api_server = TRUE;
11972   si->has_remaining_tapes = FALSE;
11973 }
11974
11975 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11976 {
11977   si->editor_cascade.el_bd              = TRUE;
11978   si->editor_cascade.el_bdx             = TRUE;
11979   si->editor_cascade.el_bdx_effects     = FALSE;
11980   si->editor_cascade.el_em              = TRUE;
11981   si->editor_cascade.el_emc             = TRUE;
11982   si->editor_cascade.el_rnd             = TRUE;
11983   si->editor_cascade.el_sb              = TRUE;
11984   si->editor_cascade.el_sp              = TRUE;
11985   si->editor_cascade.el_dc              = TRUE;
11986   si->editor_cascade.el_dx              = TRUE;
11987
11988   si->editor_cascade.el_mm              = TRUE;
11989   si->editor_cascade.el_df              = TRUE;
11990
11991   si->editor_cascade.el_chars           = FALSE;
11992   si->editor_cascade.el_steel_chars     = FALSE;
11993   si->editor_cascade.el_ce              = FALSE;
11994   si->editor_cascade.el_ge              = FALSE;
11995   si->editor_cascade.el_es              = FALSE;
11996   si->editor_cascade.el_ref             = FALSE;
11997   si->editor_cascade.el_user            = FALSE;
11998   si->editor_cascade.el_dynamic         = FALSE;
11999 }
12000
12001 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
12002
12003 static char *getHideSetupToken(void *setup_value)
12004 {
12005   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12006
12007   if (setup_value != NULL)
12008     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12009
12010   return hide_setup_token;
12011 }
12012
12013 void setHideSetupEntry(void *setup_value)
12014 {
12015   char *hide_setup_token = getHideSetupToken(setup_value);
12016
12017   if (hide_setup_hash == NULL)
12018     hide_setup_hash = newSetupFileHash();
12019
12020   if (setup_value != NULL)
12021     setHashEntry(hide_setup_hash, hide_setup_token, "");
12022 }
12023
12024 void removeHideSetupEntry(void *setup_value)
12025 {
12026   char *hide_setup_token = getHideSetupToken(setup_value);
12027
12028   if (setup_value != NULL)
12029     removeHashEntry(hide_setup_hash, hide_setup_token);
12030 }
12031
12032 boolean hideSetupEntry(void *setup_value)
12033 {
12034   char *hide_setup_token = getHideSetupToken(setup_value);
12035
12036   return (setup_value != NULL &&
12037           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12038 }
12039
12040 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12041                                       struct TokenInfo *token_info,
12042                                       int token_nr, char *token_text)
12043 {
12044   char *token_hide_text = getStringCat2(token_text, ".hide");
12045   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12046
12047   // set the value of this setup option in the setup option structure
12048   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12049
12050   // check if this setup option should be hidden in the setup menu
12051   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12052     setHideSetupEntry(token_info[token_nr].value);
12053
12054   free(token_hide_text);
12055 }
12056
12057 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12058                                       struct TokenInfo *token_info,
12059                                       int token_nr)
12060 {
12061   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12062                             token_info[token_nr].text);
12063 }
12064
12065 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12066 {
12067   int i, pnr;
12068
12069   if (!setup_file_hash)
12070     return;
12071
12072   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12073     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12074
12075   setup.touch.grid_initialized = TRUE;
12076   for (i = 0; i < 2; i++)
12077   {
12078     int grid_xsize = setup.touch.grid_xsize[i];
12079     int grid_ysize = setup.touch.grid_ysize[i];
12080     int x, y;
12081
12082     // if virtual buttons are not loaded from setup file, repeat initializing
12083     // virtual buttons grid with default values later when video is initialized
12084     if (grid_xsize == -1 ||
12085         grid_ysize == -1)
12086     {
12087       setup.touch.grid_initialized = FALSE;
12088
12089       continue;
12090     }
12091
12092     for (y = 0; y < grid_ysize; y++)
12093     {
12094       char token_string[MAX_LINE_LEN];
12095
12096       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12097
12098       char *value_string = getHashEntry(setup_file_hash, token_string);
12099
12100       if (value_string == NULL)
12101         continue;
12102
12103       for (x = 0; x < grid_xsize; x++)
12104       {
12105         char c = value_string[x];
12106
12107         setup.touch.grid_button[i][x][y] =
12108           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12109       }
12110     }
12111   }
12112
12113   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12114     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12115
12116   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12117     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12118
12119   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12120   {
12121     char prefix[30];
12122
12123     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12124
12125     setup_input = setup.input[pnr];
12126     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12127     {
12128       char full_token[100];
12129
12130       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12131       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12132                                 full_token);
12133     }
12134     setup.input[pnr] = setup_input;
12135   }
12136
12137   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12138     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12139
12140   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12141     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12142
12143   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12144     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12145
12146   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12147     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12148
12149   setHideRelatedSetupEntries();
12150 }
12151
12152 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12153 {
12154   int i;
12155
12156   if (!setup_file_hash)
12157     return;
12158
12159   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12160     setSetupInfo(auto_setup_tokens, i,
12161                  getHashEntry(setup_file_hash,
12162                               auto_setup_tokens[i].text));
12163 }
12164
12165 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12166 {
12167   int i;
12168
12169   if (!setup_file_hash)
12170     return;
12171
12172   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12173     setSetupInfo(server_setup_tokens, i,
12174                  getHashEntry(setup_file_hash,
12175                               server_setup_tokens[i].text));
12176 }
12177
12178 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12179 {
12180   int i;
12181
12182   if (!setup_file_hash)
12183     return;
12184
12185   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12186     setSetupInfo(editor_cascade_setup_tokens, i,
12187                  getHashEntry(setup_file_hash,
12188                               editor_cascade_setup_tokens[i].text));
12189 }
12190
12191 void LoadUserNames(void)
12192 {
12193   int last_user_nr = user.nr;
12194   int i;
12195
12196   if (global.user_names != NULL)
12197   {
12198     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12199       checked_free(global.user_names[i]);
12200
12201     checked_free(global.user_names);
12202   }
12203
12204   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12205
12206   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12207   {
12208     user.nr = i;
12209
12210     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12211
12212     if (setup_file_hash)
12213     {
12214       char *player_name = getHashEntry(setup_file_hash, "player_name");
12215
12216       global.user_names[i] = getFixedUserName(player_name);
12217
12218       freeSetupFileHash(setup_file_hash);
12219     }
12220
12221     if (global.user_names[i] == NULL)
12222       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12223   }
12224
12225   user.nr = last_user_nr;
12226 }
12227
12228 void LoadSetupFromFilename(char *filename)
12229 {
12230   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12231
12232   if (setup_file_hash)
12233   {
12234     decodeSetupFileHash_Default(setup_file_hash);
12235
12236     freeSetupFileHash(setup_file_hash);
12237   }
12238   else
12239   {
12240     Debug("setup", "using default setup values");
12241   }
12242 }
12243
12244 static void LoadSetup_SpecialPostProcessing(void)
12245 {
12246   char *player_name_new;
12247
12248   // needed to work around problems with fixed length strings
12249   player_name_new = getFixedUserName(setup.player_name);
12250   free(setup.player_name);
12251   setup.player_name = player_name_new;
12252
12253   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12254   if (setup.scroll_delay == FALSE)
12255   {
12256     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12257     setup.scroll_delay = TRUE;                  // now always "on"
12258   }
12259
12260   // make sure that scroll delay value stays inside valid range
12261   setup.scroll_delay_value =
12262     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12263 }
12264
12265 void LoadSetup_Default(void)
12266 {
12267   char *filename;
12268
12269   // always start with reliable default values
12270   setSetupInfoToDefaults(&setup);
12271
12272   // try to load setup values from default setup file
12273   filename = getDefaultSetupFilename();
12274
12275   if (fileExists(filename))
12276     LoadSetupFromFilename(filename);
12277
12278   // try to load setup values from platform setup file
12279   filename = getPlatformSetupFilename();
12280
12281   if (fileExists(filename))
12282     LoadSetupFromFilename(filename);
12283
12284   // try to load setup values from user setup file
12285   filename = getSetupFilename();
12286
12287   LoadSetupFromFilename(filename);
12288
12289   LoadSetup_SpecialPostProcessing();
12290 }
12291
12292 void LoadSetup_AutoSetup(void)
12293 {
12294   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12295   SetupFileHash *setup_file_hash = NULL;
12296
12297   // always start with reliable default values
12298   setSetupInfoToDefaults_AutoSetup(&setup);
12299
12300   setup_file_hash = loadSetupFileHash(filename);
12301
12302   if (setup_file_hash)
12303   {
12304     decodeSetupFileHash_AutoSetup(setup_file_hash);
12305
12306     freeSetupFileHash(setup_file_hash);
12307   }
12308
12309   free(filename);
12310 }
12311
12312 void LoadSetup_ServerSetup(void)
12313 {
12314   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12315   SetupFileHash *setup_file_hash = NULL;
12316
12317   // always start with reliable default values
12318   setSetupInfoToDefaults_ServerSetup(&setup);
12319
12320   setup_file_hash = loadSetupFileHash(filename);
12321
12322   if (setup_file_hash)
12323   {
12324     decodeSetupFileHash_ServerSetup(setup_file_hash);
12325
12326     freeSetupFileHash(setup_file_hash);
12327   }
12328
12329   free(filename);
12330
12331   if (setup.player_uuid == NULL)
12332   {
12333     // player UUID does not yet exist in setup file
12334     setup.player_uuid = getStringCopy(getUUID());
12335     setup.player_version = 2;
12336
12337     SaveSetup_ServerSetup();
12338   }
12339 }
12340
12341 void LoadSetup_EditorCascade(void)
12342 {
12343   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12344   SetupFileHash *setup_file_hash = NULL;
12345
12346   // always start with reliable default values
12347   setSetupInfoToDefaults_EditorCascade(&setup);
12348
12349   setup_file_hash = loadSetupFileHash(filename);
12350
12351   if (setup_file_hash)
12352   {
12353     decodeSetupFileHash_EditorCascade(setup_file_hash);
12354
12355     freeSetupFileHash(setup_file_hash);
12356   }
12357
12358   free(filename);
12359 }
12360
12361 void LoadSetup(void)
12362 {
12363   LoadSetup_Default();
12364   LoadSetup_AutoSetup();
12365   LoadSetup_ServerSetup();
12366   LoadSetup_EditorCascade();
12367 }
12368
12369 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12370                                            char *mapping_line)
12371 {
12372   char mapping_guid[MAX_LINE_LEN];
12373   char *mapping_start, *mapping_end;
12374
12375   // get GUID from game controller mapping line: copy complete line
12376   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12377   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12378
12379   // get GUID from game controller mapping line: cut after GUID part
12380   mapping_start = strchr(mapping_guid, ',');
12381   if (mapping_start != NULL)
12382     *mapping_start = '\0';
12383
12384   // cut newline from game controller mapping line
12385   mapping_end = strchr(mapping_line, '\n');
12386   if (mapping_end != NULL)
12387     *mapping_end = '\0';
12388
12389   // add mapping entry to game controller mappings hash
12390   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12391 }
12392
12393 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12394                                                  char *filename)
12395 {
12396   FILE *file;
12397
12398   if (!(file = fopen(filename, MODE_READ)))
12399   {
12400     Warn("cannot read game controller mappings file '%s'", filename);
12401
12402     return;
12403   }
12404
12405   while (!feof(file))
12406   {
12407     char line[MAX_LINE_LEN];
12408
12409     if (!fgets(line, MAX_LINE_LEN, file))
12410       break;
12411
12412     addGameControllerMappingToHash(mappings_hash, line);
12413   }
12414
12415   fclose(file);
12416 }
12417
12418 void SaveSetup_Default(void)
12419 {
12420   char *filename = getSetupFilename();
12421   FILE *file;
12422   int i, pnr;
12423
12424   InitUserDataDirectory();
12425
12426   if (!(file = fopen(filename, MODE_WRITE)))
12427   {
12428     Warn("cannot write setup file '%s'", filename);
12429
12430     return;
12431   }
12432
12433   fprintFileHeader(file, SETUP_FILENAME);
12434
12435   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12436   {
12437     // just to make things nicer :)
12438     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12439         global_setup_tokens[i].value == &setup.sound                    ||
12440         global_setup_tokens[i].value == &setup.graphics_set             ||
12441         global_setup_tokens[i].value == &setup.volume_simple            ||
12442         global_setup_tokens[i].value == &setup.network_mode             ||
12443         global_setup_tokens[i].value == &setup.touch.control_type       ||
12444         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12445         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12446       fprintf(file, "\n");
12447
12448     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12449   }
12450
12451   for (i = 0; i < 2; i++)
12452   {
12453     int grid_xsize = setup.touch.grid_xsize[i];
12454     int grid_ysize = setup.touch.grid_ysize[i];
12455     int x, y;
12456
12457     fprintf(file, "\n");
12458
12459     for (y = 0; y < grid_ysize; y++)
12460     {
12461       char token_string[MAX_LINE_LEN];
12462       char value_string[MAX_LINE_LEN];
12463
12464       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12465
12466       for (x = 0; x < grid_xsize; x++)
12467       {
12468         char c = setup.touch.grid_button[i][x][y];
12469
12470         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12471       }
12472
12473       value_string[grid_xsize] = '\0';
12474
12475       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12476     }
12477   }
12478
12479   fprintf(file, "\n");
12480   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12481     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12482
12483   fprintf(file, "\n");
12484   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12485     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12486
12487   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12488   {
12489     char prefix[30];
12490
12491     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12492     fprintf(file, "\n");
12493
12494     setup_input = setup.input[pnr];
12495     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12496       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12497   }
12498
12499   fprintf(file, "\n");
12500   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12501     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12502
12503   // (internal setup values not saved to user setup file)
12504
12505   fprintf(file, "\n");
12506   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12507     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12508         setup.debug.xsn_mode != STATE_AUTO)
12509       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12510
12511   fprintf(file, "\n");
12512   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12513     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12514
12515   fclose(file);
12516
12517   SetFilePermissions(filename, PERMS_PRIVATE);
12518 }
12519
12520 void SaveSetup_AutoSetup(void)
12521 {
12522   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12523   FILE *file;
12524   int i;
12525
12526   InitUserDataDirectory();
12527
12528   if (!(file = fopen(filename, MODE_WRITE)))
12529   {
12530     Warn("cannot write auto setup file '%s'", filename);
12531
12532     free(filename);
12533
12534     return;
12535   }
12536
12537   fprintFileHeader(file, AUTOSETUP_FILENAME);
12538
12539   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12540     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12541
12542   fclose(file);
12543
12544   SetFilePermissions(filename, PERMS_PRIVATE);
12545
12546   free(filename);
12547 }
12548
12549 void SaveSetup_ServerSetup(void)
12550 {
12551   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12552   FILE *file;
12553   int i;
12554
12555   InitUserDataDirectory();
12556
12557   if (!(file = fopen(filename, MODE_WRITE)))
12558   {
12559     Warn("cannot write server setup file '%s'", filename);
12560
12561     free(filename);
12562
12563     return;
12564   }
12565
12566   fprintFileHeader(file, SERVERSETUP_FILENAME);
12567
12568   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12569   {
12570     // just to make things nicer :)
12571     if (server_setup_tokens[i].value == &setup.use_api_server)
12572       fprintf(file, "\n");
12573
12574     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12575   }
12576
12577   fclose(file);
12578
12579   SetFilePermissions(filename, PERMS_PRIVATE);
12580
12581   free(filename);
12582 }
12583
12584 void SaveSetup_EditorCascade(void)
12585 {
12586   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12587   FILE *file;
12588   int i;
12589
12590   InitUserDataDirectory();
12591
12592   if (!(file = fopen(filename, MODE_WRITE)))
12593   {
12594     Warn("cannot write editor cascade state file '%s'", filename);
12595
12596     free(filename);
12597
12598     return;
12599   }
12600
12601   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12602
12603   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12604     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12605
12606   fclose(file);
12607
12608   SetFilePermissions(filename, PERMS_PRIVATE);
12609
12610   free(filename);
12611 }
12612
12613 void SaveSetup(void)
12614 {
12615   SaveSetup_Default();
12616   SaveSetup_AutoSetup();
12617   SaveSetup_ServerSetup();
12618   SaveSetup_EditorCascade();
12619 }
12620
12621 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12622                                                   char *filename)
12623 {
12624   FILE *file;
12625
12626   if (!(file = fopen(filename, MODE_WRITE)))
12627   {
12628     Warn("cannot write game controller mappings file '%s'", filename);
12629
12630     return;
12631   }
12632
12633   BEGIN_HASH_ITERATION(mappings_hash, itr)
12634   {
12635     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12636   }
12637   END_HASH_ITERATION(mappings_hash, itr)
12638
12639   fclose(file);
12640 }
12641
12642 void SaveSetup_AddGameControllerMapping(char *mapping)
12643 {
12644   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12645   SetupFileHash *mappings_hash = newSetupFileHash();
12646
12647   InitUserDataDirectory();
12648
12649   // load existing personal game controller mappings
12650   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12651
12652   // add new mapping to personal game controller mappings
12653   addGameControllerMappingToHash(mappings_hash, mapping);
12654
12655   // save updated personal game controller mappings
12656   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12657
12658   freeSetupFileHash(mappings_hash);
12659   free(filename);
12660 }
12661
12662 void LoadCustomElementDescriptions(void)
12663 {
12664   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12665   SetupFileHash *setup_file_hash;
12666   int i;
12667
12668   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12669   {
12670     if (element_info[i].custom_description != NULL)
12671     {
12672       free(element_info[i].custom_description);
12673       element_info[i].custom_description = NULL;
12674     }
12675   }
12676
12677   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12678     return;
12679
12680   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12681   {
12682     char *token = getStringCat2(element_info[i].token_name, ".name");
12683     char *value = getHashEntry(setup_file_hash, token);
12684
12685     if (value != NULL)
12686       element_info[i].custom_description = getStringCopy(value);
12687
12688     free(token);
12689   }
12690
12691   freeSetupFileHash(setup_file_hash);
12692 }
12693
12694 static int getElementFromToken(char *token)
12695 {
12696   char *value = getHashEntry(element_token_hash, token);
12697
12698   if (value != NULL)
12699     return atoi(value);
12700
12701   Warn("unknown element token '%s'", token);
12702
12703   return EL_UNDEFINED;
12704 }
12705
12706 void FreeGlobalAnimEventInfo(void)
12707 {
12708   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12709
12710   if (gaei->event_list == NULL)
12711     return;
12712
12713   int i;
12714
12715   for (i = 0; i < gaei->num_event_lists; i++)
12716   {
12717     checked_free(gaei->event_list[i]->event_value);
12718     checked_free(gaei->event_list[i]);
12719   }
12720
12721   checked_free(gaei->event_list);
12722
12723   gaei->event_list = NULL;
12724   gaei->num_event_lists = 0;
12725 }
12726
12727 static int AddGlobalAnimEventList(void)
12728 {
12729   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12730   int list_pos = gaei->num_event_lists++;
12731
12732   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12733                                      sizeof(struct GlobalAnimEventListInfo *));
12734
12735   gaei->event_list[list_pos] =
12736     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12737
12738   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12739
12740   gaeli->event_value = NULL;
12741   gaeli->num_event_values = 0;
12742
12743   return list_pos;
12744 }
12745
12746 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12747 {
12748   // do not add empty global animation events
12749   if (event_value == ANIM_EVENT_NONE)
12750     return list_pos;
12751
12752   // if list position is undefined, create new list
12753   if (list_pos == ANIM_EVENT_UNDEFINED)
12754     list_pos = AddGlobalAnimEventList();
12755
12756   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12757   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12758   int value_pos = gaeli->num_event_values++;
12759
12760   gaeli->event_value = checked_realloc(gaeli->event_value,
12761                                        gaeli->num_event_values * sizeof(int *));
12762
12763   gaeli->event_value[value_pos] = event_value;
12764
12765   return list_pos;
12766 }
12767
12768 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12769 {
12770   if (list_pos == ANIM_EVENT_UNDEFINED)
12771     return ANIM_EVENT_NONE;
12772
12773   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12774   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12775
12776   return gaeli->event_value[value_pos];
12777 }
12778
12779 int GetGlobalAnimEventValueCount(int list_pos)
12780 {
12781   if (list_pos == ANIM_EVENT_UNDEFINED)
12782     return 0;
12783
12784   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12785   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12786
12787   return gaeli->num_event_values;
12788 }
12789
12790 // This function checks if a string <s> of the format "string1, string2, ..."
12791 // exactly contains a string <s_contained>.
12792
12793 static boolean string_has_parameter(char *s, char *s_contained)
12794 {
12795   char *substring;
12796
12797   if (s == NULL || s_contained == NULL)
12798     return FALSE;
12799
12800   if (strlen(s_contained) > strlen(s))
12801     return FALSE;
12802
12803   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12804   {
12805     char next_char = s[strlen(s_contained)];
12806
12807     // check if next character is delimiter or whitespace
12808     if (next_char == ',' || next_char == '\0' ||
12809         next_char == ' ' || next_char == '\t')
12810       return TRUE;
12811   }
12812
12813   // check if string contains another parameter string after a comma
12814   substring = strchr(s, ',');
12815   if (substring == NULL)        // string does not contain a comma
12816     return FALSE;
12817
12818   // advance string pointer to next character after the comma
12819   substring++;
12820
12821   // skip potential whitespaces after the comma
12822   while (*substring == ' ' || *substring == '\t')
12823     substring++;
12824
12825   return string_has_parameter(substring, s_contained);
12826 }
12827
12828 static int get_anim_parameter_value_ce(char *s)
12829 {
12830   char *s_ptr = s;
12831   char *pattern_1 = "ce_change:custom_";
12832   char *pattern_2 = ".page_";
12833   int pattern_1_len = strlen(pattern_1);
12834   char *matching_char = strstr(s_ptr, pattern_1);
12835   int result = ANIM_EVENT_NONE;
12836
12837   if (matching_char == NULL)
12838     return ANIM_EVENT_NONE;
12839
12840   result = ANIM_EVENT_CE_CHANGE;
12841
12842   s_ptr = matching_char + pattern_1_len;
12843
12844   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12845   if (*s_ptr >= '0' && *s_ptr <= '9')
12846   {
12847     int gic_ce_nr = (*s_ptr++ - '0');
12848
12849     if (*s_ptr >= '0' && *s_ptr <= '9')
12850     {
12851       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12852
12853       if (*s_ptr >= '0' && *s_ptr <= '9')
12854         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12855     }
12856
12857     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12858       return ANIM_EVENT_NONE;
12859
12860     // custom element stored as 0 to 255
12861     gic_ce_nr--;
12862
12863     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12864   }
12865   else
12866   {
12867     // invalid custom element number specified
12868
12869     return ANIM_EVENT_NONE;
12870   }
12871
12872   // check for change page number ("page_X" or "page_XX") (optional)
12873   if (strPrefix(s_ptr, pattern_2))
12874   {
12875     s_ptr += strlen(pattern_2);
12876
12877     if (*s_ptr >= '0' && *s_ptr <= '9')
12878     {
12879       int gic_page_nr = (*s_ptr++ - '0');
12880
12881       if (*s_ptr >= '0' && *s_ptr <= '9')
12882         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12883
12884       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12885         return ANIM_EVENT_NONE;
12886
12887       // change page stored as 1 to 32 (0 means "all change pages")
12888
12889       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12890     }
12891     else
12892     {
12893       // invalid animation part number specified
12894
12895       return ANIM_EVENT_NONE;
12896     }
12897   }
12898
12899   // discard result if next character is neither delimiter nor whitespace
12900   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12901         *s_ptr == ' ' || *s_ptr == '\t'))
12902     return ANIM_EVENT_NONE;
12903
12904   return result;
12905 }
12906
12907 static int get_anim_parameter_value(char *s)
12908 {
12909   int event_value[] =
12910   {
12911     ANIM_EVENT_CLICK,
12912     ANIM_EVENT_INIT,
12913     ANIM_EVENT_START,
12914     ANIM_EVENT_END,
12915     ANIM_EVENT_POST
12916   };
12917   char *pattern_1[] =
12918   {
12919     "click:anim_",
12920     "init:anim_",
12921     "start:anim_",
12922     "end:anim_",
12923     "post:anim_"
12924   };
12925   char *pattern_2 = ".part_";
12926   char *matching_char = NULL;
12927   char *s_ptr = s;
12928   int pattern_1_len = 0;
12929   int result = ANIM_EVENT_NONE;
12930   int i;
12931
12932   result = get_anim_parameter_value_ce(s);
12933
12934   if (result != ANIM_EVENT_NONE)
12935     return result;
12936
12937   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12938   {
12939     matching_char = strstr(s_ptr, pattern_1[i]);
12940     pattern_1_len = strlen(pattern_1[i]);
12941     result = event_value[i];
12942
12943     if (matching_char != NULL)
12944       break;
12945   }
12946
12947   if (matching_char == NULL)
12948     return ANIM_EVENT_NONE;
12949
12950   s_ptr = matching_char + pattern_1_len;
12951
12952   // check for main animation number ("anim_X" or "anim_XX")
12953   if (*s_ptr >= '0' && *s_ptr <= '9')
12954   {
12955     int gic_anim_nr = (*s_ptr++ - '0');
12956
12957     if (*s_ptr >= '0' && *s_ptr <= '9')
12958       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12959
12960     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12961       return ANIM_EVENT_NONE;
12962
12963     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12964   }
12965   else
12966   {
12967     // invalid main animation number specified
12968
12969     return ANIM_EVENT_NONE;
12970   }
12971
12972   // check for animation part number ("part_X" or "part_XX") (optional)
12973   if (strPrefix(s_ptr, pattern_2))
12974   {
12975     s_ptr += strlen(pattern_2);
12976
12977     if (*s_ptr >= '0' && *s_ptr <= '9')
12978     {
12979       int gic_part_nr = (*s_ptr++ - '0');
12980
12981       if (*s_ptr >= '0' && *s_ptr <= '9')
12982         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12983
12984       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12985         return ANIM_EVENT_NONE;
12986
12987       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12988     }
12989     else
12990     {
12991       // invalid animation part number specified
12992
12993       return ANIM_EVENT_NONE;
12994     }
12995   }
12996
12997   // discard result if next character is neither delimiter nor whitespace
12998   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12999         *s_ptr == ' ' || *s_ptr == '\t'))
13000     return ANIM_EVENT_NONE;
13001
13002   return result;
13003 }
13004
13005 static int get_anim_parameter_values(char *s)
13006 {
13007   int list_pos = ANIM_EVENT_UNDEFINED;
13008   int event_value = ANIM_EVENT_DEFAULT;
13009
13010   if (string_has_parameter(s, "any"))
13011     event_value |= ANIM_EVENT_ANY;
13012
13013   if (string_has_parameter(s, "click:self") ||
13014       string_has_parameter(s, "click") ||
13015       string_has_parameter(s, "self"))
13016     event_value |= ANIM_EVENT_SELF;
13017
13018   if (string_has_parameter(s, "unclick:any"))
13019     event_value |= ANIM_EVENT_UNCLICK_ANY;
13020
13021   // if animation event found, add it to global animation event list
13022   if (event_value != ANIM_EVENT_NONE)
13023     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13024
13025   while (s != NULL)
13026   {
13027     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13028     event_value = get_anim_parameter_value(s);
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     // continue with next part of the string, starting with next comma
13035     s = strchr(s + 1, ',');
13036   }
13037
13038   return list_pos;
13039 }
13040
13041 static int get_anim_action_parameter_value(char *token)
13042 {
13043   // check most common default case first to massively speed things up
13044   if (strEqual(token, ARG_UNDEFINED))
13045     return ANIM_EVENT_ACTION_NONE;
13046
13047   int result = getImageIDFromToken(token);
13048
13049   if (result == -1)
13050   {
13051     char *gfx_token = getStringCat2("gfx.", token);
13052
13053     result = getImageIDFromToken(gfx_token);
13054
13055     checked_free(gfx_token);
13056   }
13057
13058   if (result == -1)
13059   {
13060     Key key = getKeyFromX11KeyName(token);
13061
13062     if (key != KSYM_UNDEFINED)
13063       result = -(int)key;
13064   }
13065
13066   if (result == -1)
13067   {
13068     if (isURL(token))
13069     {
13070       result = get_hash_from_string(token);     // unsigned int => int
13071       result = ABS(result);                     // may be negative now
13072       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13073
13074       setHashEntry(anim_url_hash, int2str(result, 0), token);
13075     }
13076   }
13077
13078   if (result == -1)
13079     result = ANIM_EVENT_ACTION_NONE;
13080
13081   return result;
13082 }
13083
13084 int get_parameter_value(char *value_raw, char *suffix, int type)
13085 {
13086   char *value = getStringToLower(value_raw);
13087   int result = 0;       // probably a save default value
13088
13089   if (strEqual(suffix, ".direction"))
13090   {
13091     result = (strEqual(value, "left")  ? MV_LEFT :
13092               strEqual(value, "right") ? MV_RIGHT :
13093               strEqual(value, "up")    ? MV_UP :
13094               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13095   }
13096   else if (strEqual(suffix, ".position"))
13097   {
13098     result = (strEqual(value, "left")   ? POS_LEFT :
13099               strEqual(value, "right")  ? POS_RIGHT :
13100               strEqual(value, "top")    ? POS_TOP :
13101               strEqual(value, "upper")  ? POS_UPPER :
13102               strEqual(value, "middle") ? POS_MIDDLE :
13103               strEqual(value, "lower")  ? POS_LOWER :
13104               strEqual(value, "bottom") ? POS_BOTTOM :
13105               strEqual(value, "any")    ? POS_ANY :
13106               strEqual(value, "ce")     ? POS_CE :
13107               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13108               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13109   }
13110   else if (strEqual(suffix, ".align"))
13111   {
13112     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13113               strEqual(value, "right")  ? ALIGN_RIGHT :
13114               strEqual(value, "center") ? ALIGN_CENTER :
13115               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13116   }
13117   else if (strEqual(suffix, ".valign"))
13118   {
13119     result = (strEqual(value, "top")    ? VALIGN_TOP :
13120               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13121               strEqual(value, "middle") ? VALIGN_MIDDLE :
13122               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13123   }
13124   else if (strEqual(suffix, ".anim_mode"))
13125   {
13126     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13127               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13128               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13129               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13130               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13131               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13132               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13133               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13134               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13135               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13136               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13137               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13138               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13139               string_has_parameter(value, "all")        ? ANIM_ALL :
13140               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13141               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13142               ANIM_DEFAULT);
13143
13144     if (string_has_parameter(value, "once"))
13145       result |= ANIM_ONCE;
13146
13147     if (string_has_parameter(value, "reverse"))
13148       result |= ANIM_REVERSE;
13149
13150     if (string_has_parameter(value, "opaque_player"))
13151       result |= ANIM_OPAQUE_PLAYER;
13152
13153     if (string_has_parameter(value, "static_panel"))
13154       result |= ANIM_STATIC_PANEL;
13155   }
13156   else if (strEqual(suffix, ".init_event") ||
13157            strEqual(suffix, ".anim_event"))
13158   {
13159     result = get_anim_parameter_values(value);
13160   }
13161   else if (strEqual(suffix, ".init_delay_action") ||
13162            strEqual(suffix, ".anim_delay_action") ||
13163            strEqual(suffix, ".post_delay_action") ||
13164            strEqual(suffix, ".init_event_action") ||
13165            strEqual(suffix, ".anim_event_action"))
13166   {
13167     result = get_anim_action_parameter_value(value_raw);
13168   }
13169   else if (strEqual(suffix, ".class"))
13170   {
13171     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13172               get_hash_from_string(value));
13173   }
13174   else if (strEqual(suffix, ".style"))
13175   {
13176     result = STYLE_DEFAULT;
13177
13178     if (string_has_parameter(value, "accurate_borders"))
13179       result |= STYLE_ACCURATE_BORDERS;
13180
13181     if (string_has_parameter(value, "inner_corners"))
13182       result |= STYLE_INNER_CORNERS;
13183
13184     if (string_has_parameter(value, "reverse"))
13185       result |= STYLE_REVERSE;
13186
13187     if (string_has_parameter(value, "leftmost_position"))
13188       result |= STYLE_LEFTMOST_POSITION;
13189
13190     if (string_has_parameter(value, "block_clicks"))
13191       result |= STYLE_BLOCK;
13192
13193     if (string_has_parameter(value, "passthrough_clicks"))
13194       result |= STYLE_PASSTHROUGH;
13195
13196     if (string_has_parameter(value, "multiple_actions"))
13197       result |= STYLE_MULTIPLE_ACTIONS;
13198
13199     if (string_has_parameter(value, "consume_ce_event"))
13200       result |= STYLE_CONSUME_CE_EVENT;
13201   }
13202   else if (strEqual(suffix, ".fade_mode"))
13203   {
13204     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13205               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13206               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13207               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13208               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13209               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13210               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13211               FADE_MODE_DEFAULT);
13212   }
13213   else if (strEqual(suffix, ".auto_delay_unit"))
13214   {
13215     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13216               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13217               AUTO_DELAY_UNIT_DEFAULT);
13218   }
13219   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13220   {
13221     result = gfx.get_font_from_token_function(value);
13222   }
13223   else          // generic parameter of type integer or boolean
13224   {
13225     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13226               type == TYPE_INTEGER ? get_integer_from_string(value) :
13227               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13228               ARG_UNDEFINED_VALUE);
13229   }
13230
13231   free(value);
13232
13233   return result;
13234 }
13235
13236 static int get_token_parameter_value(char *token, char *value_raw)
13237 {
13238   char *suffix;
13239
13240   if (token == NULL || value_raw == NULL)
13241     return ARG_UNDEFINED_VALUE;
13242
13243   suffix = strrchr(token, '.');
13244   if (suffix == NULL)
13245     suffix = token;
13246
13247   if (strEqual(suffix, ".element"))
13248     return getElementFromToken(value_raw);
13249
13250   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13251   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13252 }
13253
13254 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13255                                      boolean ignore_defaults)
13256 {
13257   int i;
13258
13259   for (i = 0; image_config_vars[i].token != NULL; i++)
13260   {
13261     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13262
13263     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13264     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13265       continue;
13266
13267     if (value != NULL)
13268       *image_config_vars[i].value =
13269         get_token_parameter_value(image_config_vars[i].token, value);
13270   }
13271 }
13272
13273 void InitMenuDesignSettings_Static(void)
13274 {
13275   // always start with reliable default values from static default config
13276   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13277 }
13278
13279 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13280 {
13281   int i;
13282
13283   // the following initializes hierarchical values from static configuration
13284
13285   // special case: initialize "ARG_DEFAULT" values in static default config
13286   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13287   titlescreen_initial_first_default.fade_mode  =
13288     title_initial_first_default.fade_mode;
13289   titlescreen_initial_first_default.fade_delay =
13290     title_initial_first_default.fade_delay;
13291   titlescreen_initial_first_default.post_delay =
13292     title_initial_first_default.post_delay;
13293   titlescreen_initial_first_default.auto_delay =
13294     title_initial_first_default.auto_delay;
13295   titlescreen_initial_first_default.auto_delay_unit =
13296     title_initial_first_default.auto_delay_unit;
13297   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13298   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13299   titlescreen_first_default.post_delay = title_first_default.post_delay;
13300   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13301   titlescreen_first_default.auto_delay_unit =
13302     title_first_default.auto_delay_unit;
13303   titlemessage_initial_first_default.fade_mode  =
13304     title_initial_first_default.fade_mode;
13305   titlemessage_initial_first_default.fade_delay =
13306     title_initial_first_default.fade_delay;
13307   titlemessage_initial_first_default.post_delay =
13308     title_initial_first_default.post_delay;
13309   titlemessage_initial_first_default.auto_delay =
13310     title_initial_first_default.auto_delay;
13311   titlemessage_initial_first_default.auto_delay_unit =
13312     title_initial_first_default.auto_delay_unit;
13313   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13314   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13315   titlemessage_first_default.post_delay = title_first_default.post_delay;
13316   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13317   titlemessage_first_default.auto_delay_unit =
13318     title_first_default.auto_delay_unit;
13319
13320   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13321   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13322   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13323   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13324   titlescreen_initial_default.auto_delay_unit =
13325     title_initial_default.auto_delay_unit;
13326   titlescreen_default.fade_mode  = title_default.fade_mode;
13327   titlescreen_default.fade_delay = title_default.fade_delay;
13328   titlescreen_default.post_delay = title_default.post_delay;
13329   titlescreen_default.auto_delay = title_default.auto_delay;
13330   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13331   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13332   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13333   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13334   titlemessage_initial_default.auto_delay_unit =
13335     title_initial_default.auto_delay_unit;
13336   titlemessage_default.fade_mode  = title_default.fade_mode;
13337   titlemessage_default.fade_delay = title_default.fade_delay;
13338   titlemessage_default.post_delay = title_default.post_delay;
13339   titlemessage_default.auto_delay = title_default.auto_delay;
13340   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13341
13342   // special case: initialize "ARG_DEFAULT" values in static default config
13343   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13344   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13345   {
13346     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13347     titlescreen_first[i] = titlescreen_first_default;
13348     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13349     titlemessage_first[i] = titlemessage_first_default;
13350
13351     titlescreen_initial[i] = titlescreen_initial_default;
13352     titlescreen[i] = titlescreen_default;
13353     titlemessage_initial[i] = titlemessage_initial_default;
13354     titlemessage[i] = titlemessage_default;
13355   }
13356
13357   // special case: initialize "ARG_DEFAULT" values in static default config
13358   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13359   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13360   {
13361     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13362       continue;
13363
13364     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13365     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13366     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13367   }
13368
13369   // special case: initialize "ARG_DEFAULT" values in static default config
13370   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13371   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13372   {
13373     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13374     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13375     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13376
13377     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13378       continue;
13379
13380     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13381   }
13382 }
13383
13384 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13385 {
13386   static struct
13387   {
13388     struct XY *dst, *src;
13389   }
13390   game_buttons_xy[] =
13391   {
13392     { &game.button.save,        &game.button.stop       },
13393     { &game.button.pause2,      &game.button.pause      },
13394     { &game.button.load,        &game.button.play       },
13395     { &game.button.undo,        &game.button.stop       },
13396     { &game.button.redo,        &game.button.play       },
13397
13398     { NULL,                     NULL                    }
13399   };
13400   int i, j;
13401
13402   // special case: initialize later added SETUP list size from LEVELS value
13403   if (menu.list_size[GAME_MODE_SETUP] == -1)
13404     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13405
13406   // set default position for snapshot buttons to stop/pause/play buttons
13407   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13408     if ((*game_buttons_xy[i].dst).x == -1 &&
13409         (*game_buttons_xy[i].dst).y == -1)
13410       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13411
13412   // --------------------------------------------------------------------------
13413   // dynamic viewports (including playfield margins, borders and alignments)
13414   // --------------------------------------------------------------------------
13415
13416   // dynamic viewports currently only supported for landscape mode
13417   int display_width  = MAX(video.display_width, video.display_height);
13418   int display_height = MIN(video.display_width, video.display_height);
13419
13420   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13421   {
13422     struct RectWithBorder *vp_window    = &viewport.window[i];
13423     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13424     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13425     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13426     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13427     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13428     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13429     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13430
13431     // adjust window size if min/max width/height is specified
13432
13433     if (vp_window->min_width != -1)
13434     {
13435       int window_width = display_width;
13436
13437       // when using static window height, use aspect ratio of display
13438       if (vp_window->min_height == -1)
13439         window_width = vp_window->height * display_width / display_height;
13440
13441       vp_window->width = MAX(vp_window->min_width, window_width);
13442     }
13443
13444     if (vp_window->min_height != -1)
13445     {
13446       int window_height = display_height;
13447
13448       // when using static window width, use aspect ratio of display
13449       if (vp_window->min_width == -1)
13450         window_height = vp_window->width * display_height / display_width;
13451
13452       vp_window->height = MAX(vp_window->min_height, window_height);
13453     }
13454
13455     if (vp_window->max_width != -1)
13456       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13457
13458     if (vp_window->max_height != -1)
13459       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13460
13461     int playfield_width  = vp_window->width;
13462     int playfield_height = vp_window->height;
13463
13464     // adjust playfield size and position according to specified margins
13465
13466     playfield_width  -= vp_playfield->margin_left;
13467     playfield_width  -= vp_playfield->margin_right;
13468
13469     playfield_height -= vp_playfield->margin_top;
13470     playfield_height -= vp_playfield->margin_bottom;
13471
13472     // adjust playfield size if min/max width/height is specified
13473
13474     if (vp_playfield->min_width != -1)
13475       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13476
13477     if (vp_playfield->min_height != -1)
13478       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13479
13480     if (vp_playfield->max_width != -1)
13481       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13482
13483     if (vp_playfield->max_height != -1)
13484       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13485
13486     // adjust playfield position according to specified alignment
13487
13488     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13489       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13490     else if (vp_playfield->align == ALIGN_CENTER)
13491       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13492     else if (vp_playfield->align == ALIGN_RIGHT)
13493       vp_playfield->x += playfield_width - vp_playfield->width;
13494
13495     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13496       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13497     else if (vp_playfield->valign == VALIGN_MIDDLE)
13498       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13499     else if (vp_playfield->valign == VALIGN_BOTTOM)
13500       vp_playfield->y += playfield_height - vp_playfield->height;
13501
13502     vp_playfield->x += vp_playfield->margin_left;
13503     vp_playfield->y += vp_playfield->margin_top;
13504
13505     // adjust individual playfield borders if only default border is specified
13506
13507     if (vp_playfield->border_left == -1)
13508       vp_playfield->border_left = vp_playfield->border_size;
13509     if (vp_playfield->border_right == -1)
13510       vp_playfield->border_right = vp_playfield->border_size;
13511     if (vp_playfield->border_top == -1)
13512       vp_playfield->border_top = vp_playfield->border_size;
13513     if (vp_playfield->border_bottom == -1)
13514       vp_playfield->border_bottom = vp_playfield->border_size;
13515
13516     // set dynamic playfield borders if borders are specified as undefined
13517     // (but only if window size was dynamic and playfield size was static)
13518
13519     if (dynamic_window_width && !dynamic_playfield_width)
13520     {
13521       if (vp_playfield->border_left == -1)
13522       {
13523         vp_playfield->border_left = (vp_playfield->x -
13524                                      vp_playfield->margin_left);
13525         vp_playfield->x     -= vp_playfield->border_left;
13526         vp_playfield->width += vp_playfield->border_left;
13527       }
13528
13529       if (vp_playfield->border_right == -1)
13530       {
13531         vp_playfield->border_right = (vp_window->width -
13532                                       vp_playfield->x -
13533                                       vp_playfield->width -
13534                                       vp_playfield->margin_right);
13535         vp_playfield->width += vp_playfield->border_right;
13536       }
13537     }
13538
13539     if (dynamic_window_height && !dynamic_playfield_height)
13540     {
13541       if (vp_playfield->border_top == -1)
13542       {
13543         vp_playfield->border_top = (vp_playfield->y -
13544                                     vp_playfield->margin_top);
13545         vp_playfield->y      -= vp_playfield->border_top;
13546         vp_playfield->height += vp_playfield->border_top;
13547       }
13548
13549       if (vp_playfield->border_bottom == -1)
13550       {
13551         vp_playfield->border_bottom = (vp_window->height -
13552                                        vp_playfield->y -
13553                                        vp_playfield->height -
13554                                        vp_playfield->margin_bottom);
13555         vp_playfield->height += vp_playfield->border_bottom;
13556       }
13557     }
13558
13559     // adjust playfield size to be a multiple of a defined alignment tile size
13560
13561     int align_size = vp_playfield->align_size;
13562     int playfield_xtiles = vp_playfield->width  / align_size;
13563     int playfield_ytiles = vp_playfield->height / align_size;
13564     int playfield_width_corrected  = playfield_xtiles * align_size;
13565     int playfield_height_corrected = playfield_ytiles * align_size;
13566     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13567                                  i == GFX_SPECIAL_ARG_EDITOR);
13568
13569     if (is_playfield_mode &&
13570         dynamic_playfield_width &&
13571         vp_playfield->width != playfield_width_corrected)
13572     {
13573       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13574
13575       vp_playfield->width = playfield_width_corrected;
13576
13577       if (vp_playfield->align == ALIGN_LEFT)
13578       {
13579         vp_playfield->border_left += playfield_xdiff;
13580       }
13581       else if (vp_playfield->align == ALIGN_RIGHT)
13582       {
13583         vp_playfield->border_right += playfield_xdiff;
13584       }
13585       else if (vp_playfield->align == ALIGN_CENTER)
13586       {
13587         int border_left_diff  = playfield_xdiff / 2;
13588         int border_right_diff = playfield_xdiff - border_left_diff;
13589
13590         vp_playfield->border_left  += border_left_diff;
13591         vp_playfield->border_right += border_right_diff;
13592       }
13593     }
13594
13595     if (is_playfield_mode &&
13596         dynamic_playfield_height &&
13597         vp_playfield->height != playfield_height_corrected)
13598     {
13599       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13600
13601       vp_playfield->height = playfield_height_corrected;
13602
13603       if (vp_playfield->valign == VALIGN_TOP)
13604       {
13605         vp_playfield->border_top += playfield_ydiff;
13606       }
13607       else if (vp_playfield->align == VALIGN_BOTTOM)
13608       {
13609         vp_playfield->border_right += playfield_ydiff;
13610       }
13611       else if (vp_playfield->align == VALIGN_MIDDLE)
13612       {
13613         int border_top_diff    = playfield_ydiff / 2;
13614         int border_bottom_diff = playfield_ydiff - border_top_diff;
13615
13616         vp_playfield->border_top    += border_top_diff;
13617         vp_playfield->border_bottom += border_bottom_diff;
13618       }
13619     }
13620
13621     // adjust door positions according to specified alignment
13622
13623     for (j = 0; j < 2; j++)
13624     {
13625       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13626
13627       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13628         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13629       else if (vp_door->align == ALIGN_CENTER)
13630         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13631       else if (vp_door->align == ALIGN_RIGHT)
13632         vp_door->x += vp_window->width - vp_door->width;
13633
13634       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13635         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13636       else if (vp_door->valign == VALIGN_MIDDLE)
13637         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13638       else if (vp_door->valign == VALIGN_BOTTOM)
13639         vp_door->y += vp_window->height - vp_door->height;
13640     }
13641   }
13642 }
13643
13644 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13645 {
13646   static struct
13647   {
13648     struct XYTileSize *dst, *src;
13649     int graphic;
13650   }
13651   editor_buttons_xy[] =
13652   {
13653     {
13654       &editor.button.element_left,      &editor.palette.element_left,
13655       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13656     },
13657     {
13658       &editor.button.element_middle,    &editor.palette.element_middle,
13659       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13660     },
13661     {
13662       &editor.button.element_right,     &editor.palette.element_right,
13663       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13664     },
13665
13666     { NULL,                     NULL                    }
13667   };
13668   int i;
13669
13670   // set default position for element buttons to element graphics
13671   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13672   {
13673     if ((*editor_buttons_xy[i].dst).x == -1 &&
13674         (*editor_buttons_xy[i].dst).y == -1)
13675     {
13676       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13677
13678       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13679
13680       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13681     }
13682   }
13683
13684   // adjust editor palette rows and columns if specified to be dynamic
13685
13686   if (editor.palette.cols == -1)
13687   {
13688     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13689     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13690     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13691
13692     editor.palette.cols = (vp_width - sc_width) / bt_width;
13693
13694     if (editor.palette.x == -1)
13695     {
13696       int palette_width = editor.palette.cols * bt_width + sc_width;
13697
13698       editor.palette.x = (vp_width - palette_width) / 2;
13699     }
13700   }
13701
13702   if (editor.palette.rows == -1)
13703   {
13704     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13705     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13706     int tx_height = getFontHeight(FONT_TEXT_2);
13707
13708     editor.palette.rows = (vp_height - tx_height) / bt_height;
13709
13710     if (editor.palette.y == -1)
13711     {
13712       int palette_height = editor.palette.rows * bt_height + tx_height;
13713
13714       editor.palette.y = (vp_height - palette_height) / 2;
13715     }
13716   }
13717 }
13718
13719 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13720                                                       boolean initialize)
13721 {
13722   // special case: check if network and preview player positions are redefined,
13723   // to compare this later against the main menu level preview being redefined
13724   struct TokenIntPtrInfo menu_config_players[] =
13725   {
13726     { "main.network_players.x", &menu.main.network_players.redefined    },
13727     { "main.network_players.y", &menu.main.network_players.redefined    },
13728     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13729     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13730     { "preview.x",              &preview.redefined                      },
13731     { "preview.y",              &preview.redefined                      }
13732   };
13733   int i;
13734
13735   if (initialize)
13736   {
13737     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13738       *menu_config_players[i].value = FALSE;
13739   }
13740   else
13741   {
13742     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13743       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13744         *menu_config_players[i].value = TRUE;
13745   }
13746 }
13747
13748 static void InitMenuDesignSettings_PreviewPlayers(void)
13749 {
13750   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13751 }
13752
13753 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13754 {
13755   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13756 }
13757
13758 static void LoadMenuDesignSettingsFromFilename(char *filename)
13759 {
13760   static struct TitleFadingInfo tfi;
13761   static struct TitleMessageInfo tmi;
13762   static struct TokenInfo title_tokens[] =
13763   {
13764     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13765     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13766     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13767     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13768     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13769
13770     { -1,               NULL,                   NULL                    }
13771   };
13772   static struct TokenInfo titlemessage_tokens[] =
13773   {
13774     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13775     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13776     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13777     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13778     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13779     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13780     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13781     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13782     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13783     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13784     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13785     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13786     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13787     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13788     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13789     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13790     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13791     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13792
13793     { -1,               NULL,                   NULL                    }
13794   };
13795   static struct
13796   {
13797     struct TitleFadingInfo *info;
13798     char *text;
13799   }
13800   title_info[] =
13801   {
13802     // initialize first titles from "enter screen" definitions, if defined
13803     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13804     { &title_first_default,             "menu.enter_screen.TITLE"       },
13805
13806     // initialize title screens from "next screen" definitions, if defined
13807     { &title_initial_default,           "menu.next_screen.TITLE"        },
13808     { &title_default,                   "menu.next_screen.TITLE"        },
13809
13810     { NULL,                             NULL                            }
13811   };
13812   static struct
13813   {
13814     struct TitleMessageInfo *array;
13815     char *text;
13816   }
13817   titlemessage_arrays[] =
13818   {
13819     // initialize first titles from "enter screen" definitions, if defined
13820     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13821     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13822     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13823     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13824
13825     // initialize titles from "next screen" definitions, if defined
13826     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13827     { titlescreen,                      "menu.next_screen.TITLE"        },
13828     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13829     { titlemessage,                     "menu.next_screen.TITLE"        },
13830
13831     // overwrite titles with title definitions, if defined
13832     { titlescreen_initial_first,        "[title_initial]"               },
13833     { titlescreen_first,                "[title]"                       },
13834     { titlemessage_initial_first,       "[title_initial]"               },
13835     { titlemessage_first,               "[title]"                       },
13836
13837     { titlescreen_initial,              "[title_initial]"               },
13838     { titlescreen,                      "[title]"                       },
13839     { titlemessage_initial,             "[title_initial]"               },
13840     { titlemessage,                     "[title]"                       },
13841
13842     // overwrite titles with title screen/message definitions, if defined
13843     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13844     { titlescreen_first,                "[titlescreen]"                 },
13845     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13846     { titlemessage_first,               "[titlemessage]"                },
13847
13848     { titlescreen_initial,              "[titlescreen_initial]"         },
13849     { titlescreen,                      "[titlescreen]"                 },
13850     { titlemessage_initial,             "[titlemessage_initial]"        },
13851     { titlemessage,                     "[titlemessage]"                },
13852
13853     { NULL,                             NULL                            }
13854   };
13855   SetupFileHash *setup_file_hash;
13856   int i, j, k;
13857
13858   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13859     return;
13860
13861   // the following initializes hierarchical values from dynamic configuration
13862
13863   // special case: initialize with default values that may be overwritten
13864   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13865   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13866   {
13867     struct TokenIntPtrInfo menu_config[] =
13868     {
13869       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13870       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13871       { "menu.list_size",       &menu.list_size[i]      }
13872     };
13873
13874     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13875     {
13876       char *token = menu_config[j].token;
13877       char *value = getHashEntry(setup_file_hash, token);
13878
13879       if (value != NULL)
13880         *menu_config[j].value = get_integer_from_string(value);
13881     }
13882   }
13883
13884   // special case: initialize with default values that may be overwritten
13885   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13886   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13887   {
13888     struct TokenIntPtrInfo menu_config[] =
13889     {
13890       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13891       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13892       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13893       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13894       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13895     };
13896
13897     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13898     {
13899       char *token = menu_config[j].token;
13900       char *value = getHashEntry(setup_file_hash, token);
13901
13902       if (value != NULL)
13903         *menu_config[j].value = get_integer_from_string(value);
13904     }
13905   }
13906
13907   // special case: initialize with default values that may be overwritten
13908   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13909   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13910   {
13911     struct TokenIntPtrInfo menu_config[] =
13912     {
13913       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13914       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13915     };
13916
13917     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13918     {
13919       char *token = menu_config[j].token;
13920       char *value = getHashEntry(setup_file_hash, token);
13921
13922       if (value != NULL)
13923         *menu_config[j].value = get_integer_from_string(value);
13924     }
13925   }
13926
13927   // special case: initialize with default values that may be overwritten
13928   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13929   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13930   {
13931     struct TokenIntPtrInfo menu_config[] =
13932     {
13933       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13934       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13935       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13936       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13937       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13938       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13939       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13940       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13941       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13942       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13943     };
13944
13945     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13946     {
13947       char *token = menu_config[j].token;
13948       char *value = getHashEntry(setup_file_hash, token);
13949
13950       if (value != NULL)
13951         *menu_config[j].value = get_integer_from_string(value);
13952     }
13953   }
13954
13955   // special case: initialize with default values that may be overwritten
13956   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13957   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13958   {
13959     struct TokenIntPtrInfo menu_config[] =
13960     {
13961       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13962       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13963       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13964       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13965       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13966       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13967       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13968       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13969       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13970     };
13971
13972     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13973     {
13974       char *token = menu_config[j].token;
13975       char *value = getHashEntry(setup_file_hash, token);
13976
13977       if (value != NULL)
13978         *menu_config[j].value = get_token_parameter_value(token, value);
13979     }
13980   }
13981
13982   // special case: initialize with default values that may be overwritten
13983   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13984   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13985   {
13986     struct
13987     {
13988       char *token_prefix;
13989       struct RectWithBorder *struct_ptr;
13990     }
13991     vp_struct[] =
13992     {
13993       { "viewport.window",      &viewport.window[i]     },
13994       { "viewport.playfield",   &viewport.playfield[i]  },
13995       { "viewport.door_1",      &viewport.door_1[i]     },
13996       { "viewport.door_2",      &viewport.door_2[i]     }
13997     };
13998
13999     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14000     {
14001       struct TokenIntPtrInfo vp_config[] =
14002       {
14003         { ".x",                 &vp_struct[j].struct_ptr->x             },
14004         { ".y",                 &vp_struct[j].struct_ptr->y             },
14005         { ".width",             &vp_struct[j].struct_ptr->width         },
14006         { ".height",            &vp_struct[j].struct_ptr->height        },
14007         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
14008         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
14009         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
14010         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
14011         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
14012         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
14013         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
14014         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
14015         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
14016         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
14017         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
14018         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
14019         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
14020         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
14021         { ".align",             &vp_struct[j].struct_ptr->align         },
14022         { ".valign",            &vp_struct[j].struct_ptr->valign        }
14023       };
14024
14025       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14026       {
14027         char *token = getStringCat2(vp_struct[j].token_prefix,
14028                                     vp_config[k].token);
14029         char *value = getHashEntry(setup_file_hash, token);
14030
14031         if (value != NULL)
14032           *vp_config[k].value = get_token_parameter_value(token, value);
14033
14034         free(token);
14035       }
14036     }
14037   }
14038
14039   // special case: initialize with default values that may be overwritten
14040   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14041   for (i = 0; title_info[i].info != NULL; i++)
14042   {
14043     struct TitleFadingInfo *info = title_info[i].info;
14044     char *base_token = title_info[i].text;
14045
14046     for (j = 0; title_tokens[j].type != -1; j++)
14047     {
14048       char *token = getStringCat2(base_token, title_tokens[j].text);
14049       char *value = getHashEntry(setup_file_hash, token);
14050
14051       if (value != NULL)
14052       {
14053         int parameter_value = get_token_parameter_value(token, value);
14054
14055         tfi = *info;
14056
14057         *(int *)title_tokens[j].value = (int)parameter_value;
14058
14059         *info = tfi;
14060       }
14061
14062       free(token);
14063     }
14064   }
14065
14066   // special case: initialize with default values that may be overwritten
14067   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14068   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14069   {
14070     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14071     char *base_token = titlemessage_arrays[i].text;
14072
14073     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14074     {
14075       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14076       char *value = getHashEntry(setup_file_hash, token);
14077
14078       if (value != NULL)
14079       {
14080         int parameter_value = get_token_parameter_value(token, value);
14081
14082         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14083         {
14084           tmi = array[k];
14085
14086           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14087             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14088           else
14089             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14090
14091           array[k] = tmi;
14092         }
14093       }
14094
14095       free(token);
14096     }
14097   }
14098
14099   // read (and overwrite with) values that may be specified in config file
14100   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14101
14102   // special case: check if network and preview player positions are redefined
14103   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14104
14105   freeSetupFileHash(setup_file_hash);
14106 }
14107
14108 void LoadMenuDesignSettings(void)
14109 {
14110   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14111
14112   InitMenuDesignSettings_Static();
14113   InitMenuDesignSettings_SpecialPreProcessing();
14114   InitMenuDesignSettings_PreviewPlayers();
14115
14116   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14117   {
14118     // first look for special settings configured in level series config
14119     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14120
14121     if (fileExists(filename_base))
14122       LoadMenuDesignSettingsFromFilename(filename_base);
14123   }
14124
14125   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14126
14127   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14128     LoadMenuDesignSettingsFromFilename(filename_local);
14129
14130   InitMenuDesignSettings_SpecialPostProcessing();
14131 }
14132
14133 void LoadMenuDesignSettings_AfterGraphics(void)
14134 {
14135   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14136 }
14137
14138 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14139                                 boolean ignore_defaults)
14140 {
14141   int i;
14142
14143   for (i = 0; sound_config_vars[i].token != NULL; i++)
14144   {
14145     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14146
14147     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14148     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14149       continue;
14150
14151     if (value != NULL)
14152       *sound_config_vars[i].value =
14153         get_token_parameter_value(sound_config_vars[i].token, value);
14154   }
14155 }
14156
14157 void InitSoundSettings_Static(void)
14158 {
14159   // always start with reliable default values from static default config
14160   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14161 }
14162
14163 static void LoadSoundSettingsFromFilename(char *filename)
14164 {
14165   SetupFileHash *setup_file_hash;
14166
14167   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14168     return;
14169
14170   // read (and overwrite with) values that may be specified in config file
14171   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14172
14173   freeSetupFileHash(setup_file_hash);
14174 }
14175
14176 void LoadSoundSettings(void)
14177 {
14178   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14179
14180   InitSoundSettings_Static();
14181
14182   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14183   {
14184     // first look for special settings configured in level series config
14185     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14186
14187     if (fileExists(filename_base))
14188       LoadSoundSettingsFromFilename(filename_base);
14189   }
14190
14191   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14192
14193   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14194     LoadSoundSettingsFromFilename(filename_local);
14195 }
14196
14197 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14198 {
14199   char *filename = getEditorSetupFilename();
14200   SetupFileList *setup_file_list, *list;
14201   SetupFileHash *element_hash;
14202   int num_unknown_tokens = 0;
14203   int i;
14204
14205   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14206     return;
14207
14208   element_hash = newSetupFileHash();
14209
14210   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14211     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14212
14213   // determined size may be larger than needed (due to unknown elements)
14214   *num_elements = 0;
14215   for (list = setup_file_list; list != NULL; list = list->next)
14216     (*num_elements)++;
14217
14218   // add space for up to 3 more elements for padding that may be needed
14219   *num_elements += 3;
14220
14221   // free memory for old list of elements, if needed
14222   checked_free(*elements);
14223
14224   // allocate memory for new list of elements
14225   *elements = checked_malloc(*num_elements * sizeof(int));
14226
14227   *num_elements = 0;
14228   for (list = setup_file_list; list != NULL; list = list->next)
14229   {
14230     char *value = getHashEntry(element_hash, list->token);
14231
14232     if (value == NULL)          // try to find obsolete token mapping
14233     {
14234       char *mapped_token = get_mapped_token(list->token);
14235
14236       if (mapped_token != NULL)
14237       {
14238         value = getHashEntry(element_hash, mapped_token);
14239
14240         free(mapped_token);
14241       }
14242     }
14243
14244     if (value != NULL)
14245     {
14246       (*elements)[(*num_elements)++] = atoi(value);
14247     }
14248     else
14249     {
14250       if (num_unknown_tokens == 0)
14251       {
14252         Warn("---");
14253         Warn("unknown token(s) found in config file:");
14254         Warn("- config file: '%s'", filename);
14255
14256         num_unknown_tokens++;
14257       }
14258
14259       Warn("- token: '%s'", list->token);
14260     }
14261   }
14262
14263   if (num_unknown_tokens > 0)
14264     Warn("---");
14265
14266   while (*num_elements % 4)     // pad with empty elements, if needed
14267     (*elements)[(*num_elements)++] = EL_EMPTY;
14268
14269   freeSetupFileList(setup_file_list);
14270   freeSetupFileHash(element_hash);
14271
14272 #if 0
14273   for (i = 0; i < *num_elements; i++)
14274     Debug("editor", "element '%s' [%d]\n",
14275           element_info[(*elements)[i]].token_name, (*elements)[i]);
14276 #endif
14277 }
14278
14279 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14280                                                      boolean is_sound)
14281 {
14282   SetupFileHash *setup_file_hash = NULL;
14283   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14284   char *filename_music, *filename_prefix, *filename_info;
14285   struct
14286   {
14287     char *token;
14288     char **value_ptr;
14289   }
14290   token_to_value_ptr[] =
14291   {
14292     { "title_header",   &tmp_music_file_info.title_header       },
14293     { "artist_header",  &tmp_music_file_info.artist_header      },
14294     { "album_header",   &tmp_music_file_info.album_header       },
14295     { "year_header",    &tmp_music_file_info.year_header        },
14296     { "played_header",  &tmp_music_file_info.played_header      },
14297
14298     { "title",          &tmp_music_file_info.title              },
14299     { "artist",         &tmp_music_file_info.artist             },
14300     { "album",          &tmp_music_file_info.album              },
14301     { "year",           &tmp_music_file_info.year               },
14302     { "played",         &tmp_music_file_info.played             },
14303
14304     { NULL,             NULL                                    },
14305   };
14306   int i;
14307
14308   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14309                     getCustomMusicFilename(basename));
14310
14311   if (filename_music == NULL)
14312     return NULL;
14313
14314   // ---------- try to replace file extension ----------
14315
14316   filename_prefix = getStringCopy(filename_music);
14317   if (strrchr(filename_prefix, '.') != NULL)
14318     *strrchr(filename_prefix, '.') = '\0';
14319   filename_info = getStringCat2(filename_prefix, ".txt");
14320
14321   if (fileExists(filename_info))
14322     setup_file_hash = loadSetupFileHash(filename_info);
14323
14324   free(filename_prefix);
14325   free(filename_info);
14326
14327   if (setup_file_hash == NULL)
14328   {
14329     // ---------- try to add file extension ----------
14330
14331     filename_prefix = getStringCopy(filename_music);
14332     filename_info = getStringCat2(filename_prefix, ".txt");
14333
14334     if (fileExists(filename_info))
14335       setup_file_hash = loadSetupFileHash(filename_info);
14336
14337     free(filename_prefix);
14338     free(filename_info);
14339   }
14340
14341   if (setup_file_hash == NULL)
14342     return NULL;
14343
14344   // ---------- music file info found ----------
14345
14346   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14347
14348   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14349   {
14350     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14351
14352     *token_to_value_ptr[i].value_ptr =
14353       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14354   }
14355
14356   tmp_music_file_info.basename = getStringCopy(basename);
14357   tmp_music_file_info.music = music;
14358   tmp_music_file_info.is_sound = is_sound;
14359
14360   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14361   *new_music_file_info = tmp_music_file_info;
14362
14363   return new_music_file_info;
14364 }
14365
14366 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14367 {
14368   return get_music_file_info_ext(basename, music, FALSE);
14369 }
14370
14371 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14372 {
14373   return get_music_file_info_ext(basename, sound, TRUE);
14374 }
14375
14376 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14377                                      char *basename, boolean is_sound)
14378 {
14379   for (; list != NULL; list = list->next)
14380     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14381       return TRUE;
14382
14383   return FALSE;
14384 }
14385
14386 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14387 {
14388   return music_info_listed_ext(list, basename, FALSE);
14389 }
14390
14391 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14392 {
14393   return music_info_listed_ext(list, basename, TRUE);
14394 }
14395
14396 void LoadMusicInfo(void)
14397 {
14398   int num_music_noconf = getMusicListSize_NoConf();
14399   int num_music = getMusicListSize();
14400   int num_sounds = getSoundListSize();
14401   struct FileInfo *music, *sound;
14402   struct MusicFileInfo *next, **new;
14403
14404   int i;
14405
14406   while (music_file_info != NULL)
14407   {
14408     next = music_file_info->next;
14409
14410     checked_free(music_file_info->basename);
14411
14412     checked_free(music_file_info->title_header);
14413     checked_free(music_file_info->artist_header);
14414     checked_free(music_file_info->album_header);
14415     checked_free(music_file_info->year_header);
14416     checked_free(music_file_info->played_header);
14417
14418     checked_free(music_file_info->title);
14419     checked_free(music_file_info->artist);
14420     checked_free(music_file_info->album);
14421     checked_free(music_file_info->year);
14422     checked_free(music_file_info->played);
14423
14424     free(music_file_info);
14425
14426     music_file_info = next;
14427   }
14428
14429   new = &music_file_info;
14430
14431   // get (configured or unconfigured) music file info for all levels
14432   for (i = leveldir_current->first_level;
14433        i <= leveldir_current->last_level; i++)
14434   {
14435     int music_nr;
14436
14437     if (levelset.music[i] != MUS_UNDEFINED)
14438     {
14439       // get music file info for configured level music
14440       music_nr = levelset.music[i];
14441     }
14442     else if (num_music_noconf > 0)
14443     {
14444       // get music file info for unconfigured level music
14445       int level_pos = i - leveldir_current->first_level;
14446
14447       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14448     }
14449     else
14450     {
14451       continue;
14452     }
14453
14454     char *basename = getMusicInfoEntryFilename(music_nr);
14455
14456     if (basename == NULL)
14457       continue;
14458
14459     if (!music_info_listed(music_file_info, basename))
14460     {
14461       *new = get_music_file_info(basename, music_nr);
14462
14463       if (*new != NULL)
14464         new = &(*new)->next;
14465     }
14466   }
14467
14468   // get music file info for all remaining configured music files
14469   for (i = 0; i < num_music; i++)
14470   {
14471     music = getMusicListEntry(i);
14472
14473     if (music->filename == NULL)
14474       continue;
14475
14476     if (strEqual(music->filename, UNDEFINED_FILENAME))
14477       continue;
14478
14479     // a configured file may be not recognized as music
14480     if (!FileIsMusic(music->filename))
14481       continue;
14482
14483     if (!music_info_listed(music_file_info, music->filename))
14484     {
14485       *new = get_music_file_info(music->filename, i);
14486
14487       if (*new != NULL)
14488         new = &(*new)->next;
14489     }
14490   }
14491
14492   // get sound file info for all configured sound files
14493   for (i = 0; i < num_sounds; i++)
14494   {
14495     sound = getSoundListEntry(i);
14496
14497     if (sound->filename == NULL)
14498       continue;
14499
14500     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14501       continue;
14502
14503     // a configured file may be not recognized as sound
14504     if (!FileIsSound(sound->filename))
14505       continue;
14506
14507     if (!sound_info_listed(music_file_info, sound->filename))
14508     {
14509       *new = get_sound_file_info(sound->filename, i);
14510       if (*new != NULL)
14511         new = &(*new)->next;
14512     }
14513   }
14514
14515   // add pointers to previous list nodes
14516
14517   struct MusicFileInfo *node = music_file_info;
14518
14519   while (node != NULL)
14520   {
14521     if (node->next)
14522       node->next->prev = node;
14523
14524     node = node->next;
14525   }
14526 }
14527
14528 static void add_helpanim_entry(int element, int action, int direction,
14529                                int delay, int *num_list_entries)
14530 {
14531   struct HelpAnimInfo *new_list_entry;
14532   (*num_list_entries)++;
14533
14534   helpanim_info =
14535     checked_realloc(helpanim_info,
14536                     *num_list_entries * sizeof(struct HelpAnimInfo));
14537   new_list_entry = &helpanim_info[*num_list_entries - 1];
14538
14539   new_list_entry->element = element;
14540   new_list_entry->action = action;
14541   new_list_entry->direction = direction;
14542   new_list_entry->delay = delay;
14543 }
14544
14545 static void print_unknown_token(char *filename, char *token, int token_nr)
14546 {
14547   if (token_nr == 0)
14548   {
14549     Warn("---");
14550     Warn("unknown token(s) found in config file:");
14551     Warn("- config file: '%s'", filename);
14552   }
14553
14554   Warn("- token: '%s'", token);
14555 }
14556
14557 static void print_unknown_token_end(int token_nr)
14558 {
14559   if (token_nr > 0)
14560     Warn("---");
14561 }
14562
14563 void LoadHelpAnimInfo(void)
14564 {
14565   char *filename = getHelpAnimFilename();
14566   SetupFileList *setup_file_list = NULL, *list;
14567   SetupFileHash *element_hash, *action_hash, *direction_hash;
14568   int num_list_entries = 0;
14569   int num_unknown_tokens = 0;
14570   int i;
14571
14572   if (fileExists(filename))
14573     setup_file_list = loadSetupFileList(filename);
14574
14575   if (setup_file_list == NULL)
14576   {
14577     // use reliable default values from static configuration
14578     SetupFileList *insert_ptr;
14579
14580     insert_ptr = setup_file_list =
14581       newSetupFileList(helpanim_config[0].token,
14582                        helpanim_config[0].value);
14583
14584     for (i = 1; helpanim_config[i].token; i++)
14585       insert_ptr = addListEntry(insert_ptr,
14586                                 helpanim_config[i].token,
14587                                 helpanim_config[i].value);
14588   }
14589
14590   element_hash   = newSetupFileHash();
14591   action_hash    = newSetupFileHash();
14592   direction_hash = newSetupFileHash();
14593
14594   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14595     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14596
14597   for (i = 0; i < NUM_ACTIONS; i++)
14598     setHashEntry(action_hash, element_action_info[i].suffix,
14599                  i_to_a(element_action_info[i].value));
14600
14601   // do not store direction index (bit) here, but direction value!
14602   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14603     setHashEntry(direction_hash, element_direction_info[i].suffix,
14604                  i_to_a(1 << element_direction_info[i].value));
14605
14606   for (list = setup_file_list; list != NULL; list = list->next)
14607   {
14608     char *element_token, *action_token, *direction_token;
14609     char *element_value, *action_value, *direction_value;
14610     int delay = atoi(list->value);
14611
14612     if (strEqual(list->token, "end"))
14613     {
14614       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14615
14616       continue;
14617     }
14618
14619     /* first try to break element into element/action/direction parts;
14620        if this does not work, also accept combined "element[.act][.dir]"
14621        elements (like "dynamite.active"), which are unique elements */
14622
14623     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14624     {
14625       element_value = getHashEntry(element_hash, list->token);
14626       if (element_value != NULL)        // element found
14627         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14628                            &num_list_entries);
14629       else
14630       {
14631         // no further suffixes found -- this is not an element
14632         print_unknown_token(filename, list->token, num_unknown_tokens++);
14633       }
14634
14635       continue;
14636     }
14637
14638     // token has format "<prefix>.<something>"
14639
14640     action_token = strchr(list->token, '.');    // suffix may be action ...
14641     direction_token = action_token;             // ... or direction
14642
14643     element_token = getStringCopy(list->token);
14644     *strchr(element_token, '.') = '\0';
14645
14646     element_value = getHashEntry(element_hash, element_token);
14647
14648     if (element_value == NULL)          // this is no element
14649     {
14650       element_value = getHashEntry(element_hash, list->token);
14651       if (element_value != NULL)        // combined element found
14652         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14653                            &num_list_entries);
14654       else
14655         print_unknown_token(filename, list->token, num_unknown_tokens++);
14656
14657       free(element_token);
14658
14659       continue;
14660     }
14661
14662     action_value = getHashEntry(action_hash, action_token);
14663
14664     if (action_value != NULL)           // action found
14665     {
14666       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14667                     &num_list_entries);
14668
14669       free(element_token);
14670
14671       continue;
14672     }
14673
14674     direction_value = getHashEntry(direction_hash, direction_token);
14675
14676     if (direction_value != NULL)        // direction found
14677     {
14678       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14679                          &num_list_entries);
14680
14681       free(element_token);
14682
14683       continue;
14684     }
14685
14686     if (strchr(action_token + 1, '.') == NULL)
14687     {
14688       // no further suffixes found -- this is not an action nor direction
14689
14690       element_value = getHashEntry(element_hash, list->token);
14691       if (element_value != NULL)        // combined element found
14692         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14693                            &num_list_entries);
14694       else
14695         print_unknown_token(filename, list->token, num_unknown_tokens++);
14696
14697       free(element_token);
14698
14699       continue;
14700     }
14701
14702     // token has format "<prefix>.<suffix>.<something>"
14703
14704     direction_token = strchr(action_token + 1, '.');
14705
14706     action_token = getStringCopy(action_token);
14707     *strchr(action_token + 1, '.') = '\0';
14708
14709     action_value = getHashEntry(action_hash, action_token);
14710
14711     if (action_value == NULL)           // this is no action
14712     {
14713       element_value = getHashEntry(element_hash, list->token);
14714       if (element_value != NULL)        // combined element found
14715         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14716                            &num_list_entries);
14717       else
14718         print_unknown_token(filename, list->token, num_unknown_tokens++);
14719
14720       free(element_token);
14721       free(action_token);
14722
14723       continue;
14724     }
14725
14726     direction_value = getHashEntry(direction_hash, direction_token);
14727
14728     if (direction_value != NULL)        // direction found
14729     {
14730       add_helpanim_entry(atoi(element_value), atoi(action_value),
14731                          atoi(direction_value), delay, &num_list_entries);
14732
14733       free(element_token);
14734       free(action_token);
14735
14736       continue;
14737     }
14738
14739     // this is no direction
14740
14741     element_value = getHashEntry(element_hash, list->token);
14742     if (element_value != NULL)          // combined element found
14743       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14744                          &num_list_entries);
14745     else
14746       print_unknown_token(filename, list->token, num_unknown_tokens++);
14747
14748     free(element_token);
14749     free(action_token);
14750   }
14751
14752   print_unknown_token_end(num_unknown_tokens);
14753
14754   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14755   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14756
14757   freeSetupFileList(setup_file_list);
14758   freeSetupFileHash(element_hash);
14759   freeSetupFileHash(action_hash);
14760   freeSetupFileHash(direction_hash);
14761
14762 #if 0
14763   for (i = 0; i < num_list_entries; i++)
14764     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14765           EL_NAME(helpanim_info[i].element),
14766           helpanim_info[i].element,
14767           helpanim_info[i].action,
14768           helpanim_info[i].direction,
14769           helpanim_info[i].delay);
14770 #endif
14771 }
14772
14773 void LoadHelpTextInfo(void)
14774 {
14775   char *filename = getHelpTextFilename();
14776   int i;
14777
14778   if (helptext_info != NULL)
14779   {
14780     freeSetupFileHash(helptext_info);
14781     helptext_info = NULL;
14782   }
14783
14784   if (fileExists(filename))
14785     helptext_info = loadSetupFileHash(filename);
14786
14787   if (helptext_info == NULL)
14788   {
14789     // use reliable default values from static configuration
14790     helptext_info = newSetupFileHash();
14791
14792     for (i = 0; helptext_config[i].token; i++)
14793       setHashEntry(helptext_info,
14794                    helptext_config[i].token,
14795                    helptext_config[i].value);
14796   }
14797
14798 #if 0
14799   BEGIN_HASH_ITERATION(helptext_info, itr)
14800   {
14801     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14802           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14803   }
14804   END_HASH_ITERATION(hash, itr)
14805 #endif
14806 }
14807
14808
14809 // ----------------------------------------------------------------------------
14810 // convert levels
14811 // ----------------------------------------------------------------------------
14812
14813 #define MAX_NUM_CONVERT_LEVELS          1000
14814
14815 void ConvertLevels(void)
14816 {
14817   static LevelDirTree *convert_leveldir = NULL;
14818   static int convert_level_nr = -1;
14819   static int num_levels_handled = 0;
14820   static int num_levels_converted = 0;
14821   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14822   int i;
14823
14824   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14825                                                global.convert_leveldir);
14826
14827   if (convert_leveldir == NULL)
14828     Fail("no such level identifier: '%s'", global.convert_leveldir);
14829
14830   leveldir_current = convert_leveldir;
14831
14832   if (global.convert_level_nr != -1)
14833   {
14834     convert_leveldir->first_level = global.convert_level_nr;
14835     convert_leveldir->last_level  = global.convert_level_nr;
14836   }
14837
14838   convert_level_nr = convert_leveldir->first_level;
14839
14840   PrintLine("=", 79);
14841   Print("Converting levels\n");
14842   PrintLine("-", 79);
14843   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14844   Print("Level series name:       '%s'\n", convert_leveldir->name);
14845   Print("Level series author:     '%s'\n", convert_leveldir->author);
14846   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14847   PrintLine("=", 79);
14848   Print("\n");
14849
14850   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14851     levels_failed[i] = FALSE;
14852
14853   while (convert_level_nr <= convert_leveldir->last_level)
14854   {
14855     char *level_filename;
14856     boolean new_level;
14857
14858     level_nr = convert_level_nr++;
14859
14860     Print("Level %03d: ", level_nr);
14861
14862     LoadLevel(level_nr);
14863     if (level.no_level_file || level.no_valid_file)
14864     {
14865       Print("(no level)\n");
14866       continue;
14867     }
14868
14869     Print("converting level ... ");
14870
14871 #if 0
14872     // special case: conversion of some EMC levels as requested by ACME
14873     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14874 #endif
14875
14876     level_filename = getDefaultLevelFilename(level_nr);
14877     new_level = !fileExists(level_filename);
14878
14879     if (new_level)
14880     {
14881       SaveLevel(level_nr);
14882
14883       num_levels_converted++;
14884
14885       Print("converted.\n");
14886     }
14887     else
14888     {
14889       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14890         levels_failed[level_nr] = TRUE;
14891
14892       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14893     }
14894
14895     num_levels_handled++;
14896   }
14897
14898   Print("\n");
14899   PrintLine("=", 79);
14900   Print("Number of levels handled: %d\n", num_levels_handled);
14901   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14902          (num_levels_handled ?
14903           num_levels_converted * 100 / num_levels_handled : 0));
14904   PrintLine("-", 79);
14905   Print("Summary (for automatic parsing by scripts):\n");
14906   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14907          convert_leveldir->identifier, num_levels_converted,
14908          num_levels_handled,
14909          (num_levels_handled ?
14910           num_levels_converted * 100 / num_levels_handled : 0));
14911
14912   if (num_levels_handled != num_levels_converted)
14913   {
14914     Print(", FAILED:");
14915     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14916       if (levels_failed[i])
14917         Print(" %03d", i);
14918   }
14919
14920   Print("\n");
14921   PrintLine("=", 79);
14922
14923   CloseAllAndExit(0);
14924 }
14925
14926
14927 // ----------------------------------------------------------------------------
14928 // create and save images for use in level sketches (raw BMP format)
14929 // ----------------------------------------------------------------------------
14930
14931 void CreateLevelSketchImages(void)
14932 {
14933   Bitmap *bitmap1;
14934   Bitmap *bitmap2;
14935   int i;
14936
14937   InitElementPropertiesGfxElement();
14938
14939   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14940   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14941
14942   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14943   {
14944     int element = getMappedElement(i);
14945     char basename1[16];
14946     char basename2[16];
14947     char *filename1;
14948     char *filename2;
14949
14950     sprintf(basename1, "%04d.bmp", i);
14951     sprintf(basename2, "%04ds.bmp", i);
14952
14953     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14954     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14955
14956     DrawSizedElement(0, 0, element, TILESIZE);
14957     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14958
14959     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14960       Fail("cannot save level sketch image file '%s'", filename1);
14961
14962     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14963     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14964
14965     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14966       Fail("cannot save level sketch image file '%s'", filename2);
14967
14968     free(filename1);
14969     free(filename2);
14970
14971     // create corresponding SQL statements (for normal and small images)
14972     if (i < 1000)
14973     {
14974       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14975       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14976     }
14977
14978     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14979     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14980
14981     // optional: create content for forum level sketch demonstration post
14982     if (options.debug)
14983       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14984   }
14985
14986   FreeBitmap(bitmap1);
14987   FreeBitmap(bitmap2);
14988
14989   if (options.debug)
14990     fprintf(stderr, "\n");
14991
14992   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14993
14994   CloseAllAndExit(0);
14995 }
14996
14997
14998 // ----------------------------------------------------------------------------
14999 // create and save images for element collecting animations (raw BMP format)
15000 // ----------------------------------------------------------------------------
15001
15002 static boolean createCollectImage(int element)
15003 {
15004   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15005 }
15006
15007 void CreateCollectElementImages(void)
15008 {
15009   int i, j;
15010   int num_steps = 8;
15011   int anim_frames = num_steps - 1;
15012   int tile_size = TILESIZE;
15013   int anim_width  = tile_size * anim_frames;
15014   int anim_height = tile_size;
15015   int num_collect_images = 0;
15016   int pos_collect_images = 0;
15017
15018   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15019     if (createCollectImage(i))
15020       num_collect_images++;
15021
15022   Info("Creating %d element collecting animation images ...",
15023        num_collect_images);
15024
15025   int dst_width  = anim_width * 2;
15026   int dst_height = anim_height * num_collect_images / 2;
15027   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15028   char *basename_bmp = "RocksCollect.bmp";
15029   char *basename_png = "RocksCollect.png";
15030   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15031   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15032   int len_filename_bmp = strlen(filename_bmp);
15033   int len_filename_png = strlen(filename_png);
15034   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15035   char cmd_convert[max_command_len];
15036
15037   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15038            filename_bmp,
15039            filename_png);
15040
15041   // force using RGBA surface for destination bitmap
15042   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15043                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15044
15045   dst_bitmap->surface =
15046     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15047
15048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15049   {
15050     if (!createCollectImage(i))
15051       continue;
15052
15053     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15054     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15055     int graphic = el2img(i);
15056     char *token_name = element_info[i].token_name;
15057     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15058     Bitmap *src_bitmap;
15059     int src_x, src_y;
15060
15061     Info("- creating collecting image for '%s' ...", token_name);
15062
15063     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15064
15065     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15066                tile_size, tile_size, 0, 0);
15067
15068     // force using RGBA surface for temporary bitmap (using transparent black)
15069     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15070                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15071
15072     tmp_bitmap->surface =
15073       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15074
15075     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15076
15077     for (j = 0; j < anim_frames; j++)
15078     {
15079       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15080       int frame_size = frame_size_final * num_steps;
15081       int offset = (tile_size - frame_size_final) / 2;
15082       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15083
15084       while (frame_size > frame_size_final)
15085       {
15086         frame_size /= 2;
15087
15088         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15089
15090         FreeBitmap(frame_bitmap);
15091
15092         frame_bitmap = half_bitmap;
15093       }
15094
15095       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15096                        frame_size_final, frame_size_final,
15097                        dst_x + j * tile_size + offset, dst_y + offset);
15098
15099       FreeBitmap(frame_bitmap);
15100     }
15101
15102     tmp_bitmap->surface_masked = NULL;
15103
15104     FreeBitmap(tmp_bitmap);
15105
15106     pos_collect_images++;
15107   }
15108
15109   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15110     Fail("cannot save element collecting image file '%s'", filename_bmp);
15111
15112   FreeBitmap(dst_bitmap);
15113
15114   Info("Converting image file from BMP to PNG ...");
15115
15116   if (system(cmd_convert) != 0)
15117     Fail("converting image file failed");
15118
15119   unlink(filename_bmp);
15120
15121   Info("Done.");
15122
15123   CloseAllAndExit(0);
15124 }
15125
15126
15127 // ----------------------------------------------------------------------------
15128 // create and save images for custom and group elements (raw BMP format)
15129 // ----------------------------------------------------------------------------
15130
15131 void CreateCustomElementImages(char *directory)
15132 {
15133   char *src_basename = "RocksCE-template.ilbm";
15134   char *dst_basename = "RocksCE.bmp";
15135   char *src_filename = getPath2(directory, src_basename);
15136   char *dst_filename = getPath2(directory, dst_basename);
15137   Bitmap *src_bitmap;
15138   Bitmap *bitmap;
15139   int yoffset_ce = 0;
15140   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15141   int i;
15142
15143   InitVideoDefaults();
15144
15145   ReCreateBitmap(&backbuffer, video.width, video.height);
15146
15147   src_bitmap = LoadImage(src_filename);
15148
15149   bitmap = CreateBitmap(TILEX * 16 * 2,
15150                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15151                         DEFAULT_DEPTH);
15152
15153   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15154   {
15155     int x = i % 16;
15156     int y = i / 16;
15157     int ii = i + 1;
15158     int j;
15159
15160     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15161                TILEX * x, TILEY * y + yoffset_ce);
15162
15163     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15164                TILEX, TILEY,
15165                TILEX * x + TILEX * 16,
15166                TILEY * y + yoffset_ce);
15167
15168     for (j = 2; j >= 0; j--)
15169     {
15170       int c = ii % 10;
15171
15172       BlitBitmap(src_bitmap, bitmap,
15173                  TILEX + c * 7, 0, 6, 10,
15174                  TILEX * x + 6 + j * 7,
15175                  TILEY * y + 11 + yoffset_ce);
15176
15177       BlitBitmap(src_bitmap, bitmap,
15178                  TILEX + c * 8, TILEY, 6, 10,
15179                  TILEX * 16 + TILEX * x + 6 + j * 8,
15180                  TILEY * y + 10 + yoffset_ce);
15181
15182       ii /= 10;
15183     }
15184   }
15185
15186   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15187   {
15188     int x = i % 16;
15189     int y = i / 16;
15190     int ii = i + 1;
15191     int j;
15192
15193     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15194                TILEX * x, TILEY * y + yoffset_ge);
15195
15196     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15197                TILEX, TILEY,
15198                TILEX * x + TILEX * 16,
15199                TILEY * y + yoffset_ge);
15200
15201     for (j = 1; j >= 0; j--)
15202     {
15203       int c = ii % 10;
15204
15205       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15206                  TILEX * x + 6 + j * 10,
15207                  TILEY * y + 11 + yoffset_ge);
15208
15209       BlitBitmap(src_bitmap, bitmap,
15210                  TILEX + c * 8, TILEY + 12, 6, 10,
15211                  TILEX * 16 + TILEX * x + 10 + j * 8,
15212                  TILEY * y + 10 + yoffset_ge);
15213
15214       ii /= 10;
15215     }
15216   }
15217
15218   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15219     Fail("cannot save CE graphics file '%s'", dst_filename);
15220
15221   FreeBitmap(bitmap);
15222
15223   CloseAllAndExit(0);
15224 }