added support for level color settings in BD engine to level editor
[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_BD_PLAYER,                       -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BD_PLAYER,                       -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BD_PLAYER,                       -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BD_PLAYER,                       -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BD_PLAYER,                       -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BD_PLAYER,                       -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BD_SAND,                         -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BD_SAND
677   },
678
679   {
680     EL_BD_ROCK,                         -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
683   },
684   {
685     EL_BD_ROCK,                         -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
688   },
689
690   {
691     EL_BD_DIAMOND,                      -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BD_DIAMOND,                      -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
699   },
700   {
701     EL_BD_DIAMOND,                      -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
704   },
705
706   {
707     EL_BD_FIREFLY,                      -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
710   },
711
712   {
713     EL_BD_FIREFLY_2,                    -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
716   },
717
718   {
719     EL_BD_BUTTERFLY,                    -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BD_BUTTERFLY_2,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BD_STONEFLY,                     -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
734   },
735
736   {
737     EL_BD_DRAGONFLY,                    -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
740   },
741
742   {
743     EL_BD_DIAMOND_GROWING_5,            -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
746   },
747
748   {
749     EL_BD_BOMB_EXPLODING_4,             -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
752   },
753
754   {
755     EL_BD_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_BD_EXPLODING_5,                  -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BD_MAGIC_WALL,                   -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BD_MAGIC_WALL,                   -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BD_MAGIC_WALL,                   -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BD_MAGIC_WALL,                   -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BD_MAGIC_WALL,                   -1,
788     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
790   },
791   {
792     EL_BD_MAGIC_WALL,                   -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
795   },
796   {
797     EL_BD_MAGIC_WALL,                   -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
800   },
801   {
802     EL_BD_MAGIC_WALL,                   -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
805   },
806   {
807     EL_BD_MAGIC_WALL,                   -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
810   },
811   {
812     EL_BD_MAGIC_WALL,                   -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
815   },
816   {
817     EL_BD_MAGIC_WALL,                   -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
820   },
821
822   {
823     EL_BD_CLOCK,                        -1,
824     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
825     &li.bd_clock_extra_time,            30
826   },
827
828   {
829     EL_BD_VOODOO_DOLL,                  -1,
830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
831     &li.bd_voodoo_collects_diamonds,    FALSE
832   },
833   {
834     EL_BD_VOODOO_DOLL,                  -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
836     &li.bd_voodoo_hurt_kills_player,    FALSE
837   },
838   {
839     EL_BD_VOODOO_DOLL,                  -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
841     &li.bd_voodoo_dies_by_rock,         FALSE
842   },
843   {
844     EL_BD_VOODOO_DOLL,                  -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
846     &li.bd_voodoo_vanish_by_explosion,  TRUE
847   },
848   {
849     EL_BD_VOODOO_DOLL,                  -1,
850     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
851     &li.bd_voodoo_penalty_time,         30
852   },
853
854   {
855     EL_BD_SLIME,                        -1,
856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
857     &li.bd_slime_is_predictable,        TRUE
858   },
859   {
860     EL_BD_SLIME,                        -1,
861     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
862     &li.bd_slime_permeability_rate,     100
863   },
864   {
865     EL_BD_SLIME,                        -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
867     &li.bd_slime_permeability_bits_c64, 0
868   },
869   {
870     EL_BD_SLIME,                        -1,
871     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
872     &li.bd_slime_random_seed_c64,       -1
873   },
874   {
875     EL_BD_SLIME,                        -1,
876     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
877     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
878   },
879   {
880     EL_BD_SLIME,                        -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
882     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
883   },
884   {
885     EL_BD_SLIME,                        -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
887     &li.bd_slime_eats_element_2,        EL_BD_ROCK
888   },
889   {
890     EL_BD_SLIME,                        -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
892     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
893   },
894   {
895     EL_BD_SLIME,                        -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
897     &li.bd_slime_eats_element_3,        EL_BD_NUT
898   },
899   {
900     EL_BD_SLIME,                        -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
902     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
903   },
904
905   {
906     EL_BD_ACID,                         -1,
907     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
908     &li.bd_acid_eats_element,           EL_BD_SAND
909   },
910   {
911     EL_BD_ACID,                         -1,
912     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
913     &li.bd_acid_spread_rate,            3
914   },
915   {
916     EL_BD_ACID,                         -1,
917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
918     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
919   },
920
921   {
922     EL_BD_BITER,                        -1,
923     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
924     &li.bd_biter_move_delay,            0
925   },
926   {
927     EL_BD_BITER,                        -1,
928     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
929     &li.bd_biter_eats_element,          EL_BD_DIAMOND
930   },
931
932   {
933     EL_BD_BLADDER,                      -1,
934     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
935     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
936   },
937
938   {
939     EL_BD_EXPANDABLE_WALL_ANY,          -1,
940     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
941     &li.bd_change_expanding_wall,       FALSE
942   },
943   {
944     EL_BD_EXPANDABLE_WALL_ANY,          -1,
945     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
946     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
947   },
948
949   {
950     EL_BD_REPLICATOR,                   -1,
951     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
952     &li.bd_replicators_active,          TRUE
953   },
954   {
955     EL_BD_REPLICATOR,                   -1,
956     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
957     &li.bd_replicator_create_delay,     4
958   },
959
960   {
961     EL_BD_CONVEYOR_LEFT,                -1,
962     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
963     &li.bd_conveyor_belts_active,       TRUE
964   },
965   {
966     EL_BD_CONVEYOR_LEFT,                -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
968     &li.bd_conveyor_belts_changed,      FALSE
969   },
970
971   {
972     EL_BD_WATER,                        -1,
973     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
974     &li.bd_water_cannot_flow_down,      FALSE
975   },
976
977   {
978     EL_BD_NUT,                          -1,
979     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
980     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
981   },
982
983   {
984     EL_BD_PNEUMATIC_HAMMER,             -1,
985     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
986     &li.bd_hammer_walls_break_delay,    5
987   },
988   {
989     EL_BD_PNEUMATIC_HAMMER,             -1,
990     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
991     &li.bd_hammer_walls_reappear,       FALSE
992   },
993   {
994     EL_BD_PNEUMATIC_HAMMER,             -1,
995     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
996     &li.bd_hammer_walls_reappear_delay, 100
997   },
998
999   {
1000     EL_BD_ROCKET_LAUNCHER,              -1,
1001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1002     &li.bd_infinite_rockets,            FALSE
1003   },
1004
1005   {
1006     EL_BD_SKELETON,                     -1,
1007     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1008     &li.bd_num_skeletons_needed_for_pot, 5
1009   },
1010   {
1011     EL_BD_SKELETON,                     -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1013     &li.bd_skeleton_worth_num_diamonds, 0
1014   },
1015
1016   {
1017     EL_BD_CREATURE_SWITCH,              -1,
1018     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1019     &li.bd_creatures_start_backwards,   FALSE
1020   },
1021   {
1022     EL_BD_CREATURE_SWITCH,              -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1024     &li.bd_creatures_turn_on_hatching,  FALSE
1025   },
1026   {
1027     EL_BD_CREATURE_SWITCH,              -1,
1028     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1029     &li.bd_creatures_auto_turn_delay,   0
1030   },
1031
1032   {
1033     EL_BD_GRAVITY_SWITCH,               -1,
1034     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1035     &li.bd_gravity_direction,           GD_MV_DOWN
1036   },
1037   {
1038     EL_BD_GRAVITY_SWITCH,               -1,
1039     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1040     &li.bd_gravity_switch_active,       FALSE
1041   },
1042   {
1043     EL_BD_GRAVITY_SWITCH,               -1,
1044     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1045     &li.bd_gravity_switch_delay,        10
1046   },
1047   {
1048     EL_BD_GRAVITY_SWITCH,               -1,
1049     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1050     &li.bd_gravity_affects_all,         TRUE
1051   },
1052
1053   // (the following values are related to various game elements)
1054
1055   {
1056     EL_EMERALD,                         -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.score[SC_EMERALD],              10
1059   },
1060
1061   {
1062     EL_DIAMOND,                         -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.score[SC_DIAMOND],              10
1065   },
1066
1067   {
1068     EL_BUG,                             -1,
1069     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1070     &li.score[SC_BUG],                  10
1071   },
1072
1073   {
1074     EL_SPACESHIP,                       -1,
1075     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1076     &li.score[SC_SPACESHIP],            10
1077   },
1078
1079   {
1080     EL_PACMAN,                          -1,
1081     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1082     &li.score[SC_PACMAN],               10
1083   },
1084
1085   {
1086     EL_NUT,                             -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1088     &li.score[SC_NUT],                  10
1089   },
1090
1091   {
1092     EL_DYNAMITE,                        -1,
1093     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1094     &li.score[SC_DYNAMITE],             10
1095   },
1096
1097   {
1098     EL_KEY_1,                           -1,
1099     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1100     &li.score[SC_KEY],                  10
1101   },
1102
1103   {
1104     EL_PEARL,                           -1,
1105     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1106     &li.score[SC_PEARL],                10
1107   },
1108
1109   {
1110     EL_CRYSTAL,                         -1,
1111     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1112     &li.score[SC_CRYSTAL],              10
1113   },
1114
1115   // (amoeba values used by R'n'D game engine only)
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1119     &li.amoeba_content,                 EL_DIAMOND
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1124     &li.amoeba_speed,                   10
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1129     &li.grow_into_diggable,             TRUE
1130   },
1131   // (amoeba values used by BD game engine only)
1132   {
1133     EL_BD_AMOEBA,                       -1,
1134     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1135     &li.bd_amoeba_wait_for_hatching,    FALSE
1136   },
1137   {
1138     EL_BD_AMOEBA,                       -1,
1139     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1140     &li.bd_amoeba_start_immediately,    TRUE
1141   },
1142   {
1143     EL_BD_AMOEBA,                       -1,
1144     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1145     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1146   },
1147   {
1148     EL_BD_AMOEBA,                       -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1150     &li.bd_amoeba_threshold_too_big,    200
1151   },
1152   {
1153     EL_BD_AMOEBA,                       -1,
1154     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1155     &li.bd_amoeba_slow_growth_time,     200
1156   },
1157   {
1158     EL_BD_AMOEBA,                       -1,
1159     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1160     &li.bd_amoeba_slow_growth_rate,     3
1161   },
1162   {
1163     EL_BD_AMOEBA,                       -1,
1164     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1165     &li.bd_amoeba_fast_growth_rate,     25
1166   },
1167   {
1168     EL_BD_AMOEBA,                       -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1170     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1171   },
1172   {
1173     EL_BD_AMOEBA,                       -1,
1174     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1175     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1176   },
1177
1178   {
1179     EL_BD_AMOEBA_2,                     -1,
1180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1181     &li.bd_amoeba_2_threshold_too_big,  200
1182   },
1183   {
1184     EL_BD_AMOEBA_2,                     -1,
1185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1186     &li.bd_amoeba_2_slow_growth_time,   200
1187   },
1188   {
1189     EL_BD_AMOEBA_2,                     -1,
1190     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1191     &li.bd_amoeba_2_slow_growth_rate,   3
1192   },
1193   {
1194     EL_BD_AMOEBA_2,                     -1,
1195     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1196     &li.bd_amoeba_2_fast_growth_rate,   25
1197   },
1198   {
1199     EL_BD_AMOEBA_2,                     -1,
1200     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1201     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1202   },
1203   {
1204     EL_BD_AMOEBA_2,                     -1,
1205     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1206     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1207   },
1208   {
1209     EL_BD_AMOEBA_2,                     -1,
1210     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1211     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1212   },
1213   {
1214     EL_BD_AMOEBA_2,                     -1,
1215     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1216     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1217   },
1218
1219   {
1220     EL_YAMYAM,                          -1,
1221     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1222     &li.yamyam_content,                 EL_ROCK, NULL,
1223     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1224   },
1225   {
1226     EL_YAMYAM,                          -1,
1227     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1228     &li.score[SC_YAMYAM],               10
1229   },
1230
1231   {
1232     EL_ROBOT,                           -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1234     &li.score[SC_ROBOT],                10
1235   },
1236   {
1237     EL_ROBOT,                           -1,
1238     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1239     &li.slurp_score,                    10
1240   },
1241
1242   {
1243     EL_ROBOT_WHEEL,                     -1,
1244     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1245     &li.time_wheel,                     10
1246   },
1247
1248   {
1249     EL_MAGIC_WALL,                      -1,
1250     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1251     &li.time_magic_wall,                10
1252   },
1253
1254   {
1255     EL_GAME_OF_LIFE,                    -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1257     &li.game_of_life[0],                2
1258   },
1259   {
1260     EL_GAME_OF_LIFE,                    -1,
1261     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1262     &li.game_of_life[1],                3
1263   },
1264   {
1265     EL_GAME_OF_LIFE,                    -1,
1266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1267     &li.game_of_life[2],                3
1268   },
1269   {
1270     EL_GAME_OF_LIFE,                    -1,
1271     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1272     &li.game_of_life[3],                3
1273   },
1274   {
1275     EL_GAME_OF_LIFE,                    -1,
1276     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1277     &li.use_life_bugs,                  FALSE
1278   },
1279
1280   {
1281     EL_BIOMAZE,                         -1,
1282     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1283     &li.biomaze[0],                     2
1284   },
1285   {
1286     EL_BIOMAZE,                         -1,
1287     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1288     &li.biomaze[1],                     3
1289   },
1290   {
1291     EL_BIOMAZE,                         -1,
1292     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1293     &li.biomaze[2],                     3
1294   },
1295   {
1296     EL_BIOMAZE,                         -1,
1297     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1298     &li.biomaze[3],                     3
1299   },
1300
1301   {
1302     EL_TIMEGATE_SWITCH,                 -1,
1303     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1304     &li.time_timegate,                  10
1305   },
1306
1307   {
1308     EL_LIGHT_SWITCH_ACTIVE,             -1,
1309     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1310     &li.time_light,                     10
1311   },
1312
1313   {
1314     EL_SHIELD_NORMAL,                   -1,
1315     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1316     &li.shield_normal_time,             10
1317   },
1318   {
1319     EL_SHIELD_NORMAL,                   -1,
1320     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1321     &li.score[SC_SHIELD],               10
1322   },
1323
1324   {
1325     EL_SHIELD_DEADLY,                   -1,
1326     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1327     &li.shield_deadly_time,             10
1328   },
1329   {
1330     EL_SHIELD_DEADLY,                   -1,
1331     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1332     &li.score[SC_SHIELD],               10
1333   },
1334
1335   {
1336     EL_EXTRA_TIME,                      -1,
1337     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1338     &li.extra_time,                     10
1339   },
1340   {
1341     EL_EXTRA_TIME,                      -1,
1342     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1343     &li.extra_time_score,               10
1344   },
1345
1346   {
1347     EL_TIME_ORB_FULL,                   -1,
1348     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1349     &li.time_orb_time,                  10
1350   },
1351   {
1352     EL_TIME_ORB_FULL,                   -1,
1353     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1354     &li.use_time_orb_bug,               FALSE
1355   },
1356
1357   {
1358     EL_SPRING,                          -1,
1359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1360     &li.use_spring_bug,                 FALSE
1361   },
1362
1363   {
1364     EL_EMC_ANDROID,                     -1,
1365     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1366     &li.android_move_time,              10
1367   },
1368   {
1369     EL_EMC_ANDROID,                     -1,
1370     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1371     &li.android_clone_time,             10
1372   },
1373   {
1374     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1375     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1376     &li.android_clone_element[0],       EL_EMPTY, NULL,
1377     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1378   },
1379   {
1380     EL_EMC_ANDROID,                     -1,
1381     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1382     &li.android_clone_element[0],       EL_EMPTY, NULL,
1383     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1384   },
1385
1386   {
1387     EL_EMC_LENSES,                      -1,
1388     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1389     &li.lenses_score,                   10
1390   },
1391   {
1392     EL_EMC_LENSES,                      -1,
1393     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1394     &li.lenses_time,                    10
1395   },
1396
1397   {
1398     EL_EMC_MAGNIFIER,                   -1,
1399     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1400     &li.magnify_score,                  10
1401   },
1402   {
1403     EL_EMC_MAGNIFIER,                   -1,
1404     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1405     &li.magnify_time,                   10
1406   },
1407
1408   {
1409     EL_EMC_MAGIC_BALL,                  -1,
1410     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1411     &li.ball_time,                      10
1412   },
1413   {
1414     EL_EMC_MAGIC_BALL,                  -1,
1415     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1416     &li.ball_random,                    FALSE
1417   },
1418   {
1419     EL_EMC_MAGIC_BALL,                  -1,
1420     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1421     &li.ball_active_initial,            FALSE
1422   },
1423   {
1424     EL_EMC_MAGIC_BALL,                  -1,
1425     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1426     &li.ball_content,                   EL_EMPTY, NULL,
1427     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1428   },
1429
1430   {
1431     EL_SOKOBAN_FIELD_EMPTY,             -1,
1432     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1433     &li.sb_fields_needed,               TRUE
1434   },
1435
1436   {
1437     EL_SOKOBAN_OBJECT,                  -1,
1438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1439     &li.sb_objects_needed,              TRUE
1440   },
1441
1442   {
1443     EL_MM_MCDUFFIN,                     -1,
1444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1445     &li.mm_laser_red,                   FALSE
1446   },
1447   {
1448     EL_MM_MCDUFFIN,                     -1,
1449     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1450     &li.mm_laser_green,                 FALSE
1451   },
1452   {
1453     EL_MM_MCDUFFIN,                     -1,
1454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1455     &li.mm_laser_blue,                  TRUE
1456   },
1457
1458   {
1459     EL_DF_LASER,                        -1,
1460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1461     &li.df_laser_red,                   TRUE
1462   },
1463   {
1464     EL_DF_LASER,                        -1,
1465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1466     &li.df_laser_green,                 TRUE
1467   },
1468   {
1469     EL_DF_LASER,                        -1,
1470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1471     &li.df_laser_blue,                  FALSE
1472   },
1473
1474   {
1475     EL_MM_FUSE_ACTIVE,                  -1,
1476     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1477     &li.mm_time_fuse,                   25
1478   },
1479   {
1480     EL_MM_BOMB,                         -1,
1481     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1482     &li.mm_time_bomb,                   75
1483   },
1484
1485   {
1486     EL_MM_GRAY_BALL,                    -1,
1487     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1488     &li.mm_time_ball,                   75
1489   },
1490   {
1491     EL_MM_GRAY_BALL,                    -1,
1492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1493     &li.mm_ball_choice_mode,            ANIM_RANDOM
1494   },
1495   {
1496     EL_MM_GRAY_BALL,                    -1,
1497     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1498     &li.mm_ball_content,                EL_EMPTY, NULL,
1499     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1500   },
1501   {
1502     EL_MM_GRAY_BALL,                    -1,
1503     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1504     &li.rotate_mm_ball_content,         TRUE
1505   },
1506   {
1507     EL_MM_GRAY_BALL,                    -1,
1508     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1509     &li.explode_mm_ball,                FALSE
1510   },
1511
1512   {
1513     EL_MM_STEEL_BLOCK,                  -1,
1514     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1515     &li.mm_time_block,                  75
1516   },
1517   {
1518     EL_MM_LIGHTBALL,                    -1,
1519     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1520     &li.score[SC_ELEM_BONUS],           10
1521   },
1522
1523   {
1524     -1,                                 -1,
1525     -1,                                 -1,
1526     NULL,                               -1
1527   }
1528 };
1529
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1531 {
1532   {
1533     -1,                                 -1,
1534     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1535     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1536   },
1537   {
1538     -1,                                 -1,
1539     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1540     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1541   },
1542
1543   {
1544     -1,                                 -1,
1545     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1546     &xx_envelope.autowrap,              FALSE
1547   },
1548   {
1549     -1,                                 -1,
1550     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1551     &xx_envelope.centered,              FALSE
1552   },
1553
1554   {
1555     -1,                                 -1,
1556     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1557     &xx_envelope.text,                  -1, NULL,
1558     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1559     &xx_default_string_empty[0]
1560   },
1561
1562   {
1563     -1,                                 -1,
1564     -1,                                 -1,
1565     NULL,                               -1
1566   }
1567 };
1568
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1570 {
1571   {
1572     -1,                                 -1,
1573     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1574     &xx_ei.description[0],              -1,
1575     &yy_ei.description[0],
1576     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1577     &xx_default_description[0]
1578   },
1579
1580   {
1581     -1,                                 -1,
1582     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1583     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1585   },
1586 #if ENABLE_RESERVED_CODE
1587   // (reserved for later use)
1588   {
1589     -1,                                 -1,
1590     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1591     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1593   },
1594 #endif
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1599     &xx_ei.use_gfx_element,             FALSE,
1600     &yy_ei.use_gfx_element
1601   },
1602   {
1603     -1,                                 -1,
1604     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1605     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1606     &yy_ei.gfx_element_initial
1607   },
1608
1609   {
1610     -1,                                 -1,
1611     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1612     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1613     &yy_ei.access_direction
1614   },
1615
1616   {
1617     -1,                                 -1,
1618     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1619     &xx_ei.collect_score_initial,       10,
1620     &yy_ei.collect_score_initial
1621   },
1622   {
1623     -1,                                 -1,
1624     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1625     &xx_ei.collect_count_initial,       1,
1626     &yy_ei.collect_count_initial
1627   },
1628
1629   {
1630     -1,                                 -1,
1631     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1632     &xx_ei.ce_value_fixed_initial,      0,
1633     &yy_ei.ce_value_fixed_initial
1634   },
1635   {
1636     -1,                                 -1,
1637     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1638     &xx_ei.ce_value_random_initial,     0,
1639     &yy_ei.ce_value_random_initial
1640   },
1641   {
1642     -1,                                 -1,
1643     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1644     &xx_ei.use_last_ce_value,           FALSE,
1645     &yy_ei.use_last_ce_value
1646   },
1647
1648   {
1649     -1,                                 -1,
1650     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1651     &xx_ei.push_delay_fixed,            8,
1652     &yy_ei.push_delay_fixed
1653   },
1654   {
1655     -1,                                 -1,
1656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1657     &xx_ei.push_delay_random,           8,
1658     &yy_ei.push_delay_random
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1663     &xx_ei.drop_delay_fixed,            0,
1664     &yy_ei.drop_delay_fixed
1665   },
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1669     &xx_ei.drop_delay_random,           0,
1670     &yy_ei.drop_delay_random
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1675     &xx_ei.move_delay_fixed,            0,
1676     &yy_ei.move_delay_fixed
1677   },
1678   {
1679     -1,                                 -1,
1680     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1681     &xx_ei.move_delay_random,           0,
1682     &yy_ei.move_delay_random
1683   },
1684   {
1685     -1,                                 -1,
1686     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1687     &xx_ei.step_delay_fixed,            0,
1688     &yy_ei.step_delay_fixed
1689   },
1690   {
1691     -1,                                 -1,
1692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1693     &xx_ei.step_delay_random,           0,
1694     &yy_ei.step_delay_random
1695   },
1696
1697   {
1698     -1,                                 -1,
1699     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1700     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1701     &yy_ei.move_pattern
1702   },
1703   {
1704     -1,                                 -1,
1705     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1706     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1707     &yy_ei.move_direction_initial
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1712     &xx_ei.move_stepsize,               TILEX / 8,
1713     &yy_ei.move_stepsize
1714   },
1715
1716   {
1717     -1,                                 -1,
1718     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1719     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1720     &yy_ei.move_enter_element
1721   },
1722   {
1723     -1,                                 -1,
1724     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1725     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1726     &yy_ei.move_leave_element
1727   },
1728   {
1729     -1,                                 -1,
1730     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1731     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1732     &yy_ei.move_leave_type
1733   },
1734
1735   {
1736     -1,                                 -1,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1738     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1739     &yy_ei.slippery_type
1740   },
1741
1742   {
1743     -1,                                 -1,
1744     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1745     &xx_ei.explosion_type,              EXPLODES_3X3,
1746     &yy_ei.explosion_type
1747   },
1748   {
1749     -1,                                 -1,
1750     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1751     &xx_ei.explosion_delay,             16,
1752     &yy_ei.explosion_delay
1753   },
1754   {
1755     -1,                                 -1,
1756     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1757     &xx_ei.ignition_delay,              8,
1758     &yy_ei.ignition_delay
1759   },
1760
1761   {
1762     -1,                                 -1,
1763     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1764     &xx_ei.content,                     EL_EMPTY_SPACE,
1765     &yy_ei.content,
1766     &xx_num_contents,                   1, 1
1767   },
1768
1769   // ---------- "num_change_pages" must be the last entry ---------------------
1770
1771   {
1772     -1,                                 SAVE_CONF_ALWAYS,
1773     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1774     &xx_ei.num_change_pages,            1,
1775     &yy_ei.num_change_pages
1776   },
1777
1778   {
1779     -1,                                 -1,
1780     -1,                                 -1,
1781     NULL,                               -1,
1782     NULL
1783   }
1784 };
1785
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1787 {
1788   // ---------- "current_change_page" must be the first entry -----------------
1789
1790   {
1791     -1,                                 SAVE_CONF_ALWAYS,
1792     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1793     &xx_current_change_page,            -1
1794   },
1795
1796   // ---------- (the remaining entries can be in any order) -------------------
1797
1798   {
1799     -1,                                 -1,
1800     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1801     &xx_change.can_change,              FALSE
1802   },
1803
1804   {
1805     -1,                                 -1,
1806     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1807     &xx_event_bits[0],                  0
1808   },
1809   {
1810     -1,                                 -1,
1811     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1812     &xx_event_bits[1],                  0
1813   },
1814
1815   {
1816     -1,                                 -1,
1817     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1818     &xx_change.trigger_player,          CH_PLAYER_ANY
1819   },
1820   {
1821     -1,                                 -1,
1822     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1823     &xx_change.trigger_side,            CH_SIDE_ANY
1824   },
1825   {
1826     -1,                                 -1,
1827     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1828     &xx_change.trigger_page,            CH_PAGE_ANY
1829   },
1830
1831   {
1832     -1,                                 -1,
1833     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1834     &xx_change.target_element,          EL_EMPTY_SPACE
1835   },
1836
1837   {
1838     -1,                                 -1,
1839     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1840     &xx_change.delay_fixed,             0
1841   },
1842   {
1843     -1,                                 -1,
1844     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1845     &xx_change.delay_random,            0
1846   },
1847   {
1848     -1,                                 -1,
1849     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1850     &xx_change.delay_frames,            FRAMES_PER_SECOND
1851   },
1852
1853   {
1854     -1,                                 -1,
1855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1856     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1857   },
1858
1859   {
1860     -1,                                 -1,
1861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1862     &xx_change.explode,                 FALSE
1863   },
1864   {
1865     -1,                                 -1,
1866     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1867     &xx_change.use_target_content,      FALSE
1868   },
1869   {
1870     -1,                                 -1,
1871     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1872     &xx_change.only_if_complete,        FALSE
1873   },
1874   {
1875     -1,                                 -1,
1876     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1877     &xx_change.use_random_replace,      FALSE
1878   },
1879   {
1880     -1,                                 -1,
1881     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1882     &xx_change.random_percentage,       100
1883   },
1884   {
1885     -1,                                 -1,
1886     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1887     &xx_change.replace_when,            CP_WHEN_EMPTY
1888   },
1889
1890   {
1891     -1,                                 -1,
1892     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1893     &xx_change.has_action,              FALSE
1894   },
1895   {
1896     -1,                                 -1,
1897     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1898     &xx_change.action_type,             CA_NO_ACTION
1899   },
1900   {
1901     -1,                                 -1,
1902     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1903     &xx_change.action_mode,             CA_MODE_UNDEFINED
1904   },
1905   {
1906     -1,                                 -1,
1907     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1908     &xx_change.action_arg,              CA_ARG_UNDEFINED
1909   },
1910
1911   {
1912     -1,                                 -1,
1913     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1914     &xx_change.action_element,          EL_EMPTY_SPACE
1915   },
1916
1917   {
1918     -1,                                 -1,
1919     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1920     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1921     &xx_num_contents,                   1, 1
1922   },
1923
1924   {
1925     -1,                                 -1,
1926     -1,                                 -1,
1927     NULL,                               -1
1928   }
1929 };
1930
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1932 {
1933   {
1934     -1,                                 -1,
1935     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1936     &xx_ei.description[0],              -1, NULL,
1937     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1938     &xx_default_description[0]
1939   },
1940
1941   {
1942     -1,                                 -1,
1943     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1944     &xx_ei.use_gfx_element,             FALSE
1945   },
1946   {
1947     -1,                                 -1,
1948     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1949     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1950   },
1951
1952   {
1953     -1,                                 -1,
1954     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1955     &xx_group.choice_mode,              ANIM_RANDOM
1956   },
1957
1958   {
1959     -1,                                 -1,
1960     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1961     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1962     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1963   },
1964
1965   {
1966     -1,                                 -1,
1967     -1,                                 -1,
1968     NULL,                               -1
1969   }
1970 };
1971
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1973 {
1974   {
1975     -1,                                 -1,
1976     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1977     &xx_ei.use_gfx_element,             FALSE
1978   },
1979   {
1980     -1,                                 -1,
1981     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1982     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1983   },
1984
1985   {
1986     -1,                                 -1,
1987     -1,                                 -1,
1988     NULL,                               -1
1989   }
1990 };
1991
1992 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1993 {
1994   {
1995     EL_PLAYER_1,                        -1,
1996     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1997     &li.block_snap_field,               TRUE
1998   },
1999   {
2000     EL_PLAYER_1,                        -1,
2001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2002     &li.continuous_snapping,            TRUE
2003   },
2004   {
2005     EL_PLAYER_1,                        -1,
2006     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2007     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2008   },
2009   {
2010     EL_PLAYER_1,                        -1,
2011     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2012     &li.use_start_element[0],           FALSE
2013   },
2014   {
2015     EL_PLAYER_1,                        -1,
2016     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2017     &li.start_element[0],               EL_PLAYER_1
2018   },
2019   {
2020     EL_PLAYER_1,                        -1,
2021     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2022     &li.use_artwork_element[0],         FALSE
2023   },
2024   {
2025     EL_PLAYER_1,                        -1,
2026     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2027     &li.artwork_element[0],             EL_PLAYER_1
2028   },
2029   {
2030     EL_PLAYER_1,                        -1,
2031     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2032     &li.use_explosion_element[0],       FALSE
2033   },
2034   {
2035     EL_PLAYER_1,                        -1,
2036     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2037     &li.explosion_element[0],           EL_PLAYER_1
2038   },
2039
2040   {
2041     -1,                                 -1,
2042     -1,                                 -1,
2043     NULL,                               -1
2044   }
2045 };
2046
2047 static struct
2048 {
2049   int filetype;
2050   char *id;
2051 }
2052 filetype_id_list[] =
2053 {
2054   { LEVEL_FILE_TYPE_RND,        "RND"   },
2055   { LEVEL_FILE_TYPE_BD,         "BD"    },
2056   { LEVEL_FILE_TYPE_EM,         "EM"    },
2057   { LEVEL_FILE_TYPE_SP,         "SP"    },
2058   { LEVEL_FILE_TYPE_DX,         "DX"    },
2059   { LEVEL_FILE_TYPE_SB,         "SB"    },
2060   { LEVEL_FILE_TYPE_DC,         "DC"    },
2061   { LEVEL_FILE_TYPE_MM,         "MM"    },
2062   { LEVEL_FILE_TYPE_MM,         "DF"    },
2063   { -1,                         NULL    },
2064 };
2065
2066
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2070
2071 static boolean check_special_flags(char *flag)
2072 {
2073   if (strEqual(options.special_flags, flag) ||
2074       strEqual(leveldir_current->special_flags, flag))
2075     return TRUE;
2076
2077   return FALSE;
2078 }
2079
2080 static struct DateInfo getCurrentDate(void)
2081 {
2082   time_t epoch_seconds = time(NULL);
2083   struct tm *now = localtime(&epoch_seconds);
2084   struct DateInfo date;
2085
2086   date.year  = now->tm_year + 1900;
2087   date.month = now->tm_mon  + 1;
2088   date.day   = now->tm_mday;
2089
2090   date.src   = DATE_SRC_CLOCK;
2091
2092   return date;
2093 }
2094
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2096 {
2097   int i;
2098
2099   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100     change->has_event[i] = FALSE;
2101 }
2102
2103 static void resetEventBits(void)
2104 {
2105   int i;
2106
2107   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108     xx_event_bits[i] = 0;
2109 }
2110
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2112 {
2113   int i;
2114
2115   /* important: only change event flag if corresponding event bit is set
2116      (this is because all xx_event_bits[] values are loaded separately,
2117      and all xx_event_bits[] values are set back to zero before loading
2118      another value xx_event_bits[x] (each value representing 32 flags)) */
2119
2120   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122       change->has_event[i] = TRUE;
2123 }
2124
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2126 {
2127   int i;
2128
2129   /* in contrast to the above function setEventFlagsFromEventBits(), it
2130      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131      depending on the corresponding change->has_event[i] values here, as
2132      all xx_event_bits[] values are reset in resetEventBits() before */
2133
2134   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135     if (change->has_event[i])
2136       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2137 }
2138
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2140 {
2141   static char description[MAX_ELEMENT_NAME_LEN + 1];
2142   char *default_description = (ei->custom_description != NULL ?
2143                                ei->custom_description :
2144                                ei->editor_description);
2145   int i;
2146
2147   // always start with reliable default values
2148   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149     description[i] = '\0';
2150
2151   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2153
2154   return &description[0];
2155 }
2156
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2158 {
2159   char *default_description = getDefaultElementDescription(ei);
2160   int i;
2161
2162   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163     ei->description[i] = default_description[i];
2164 }
2165
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2167 {
2168   int i;
2169
2170   for (i = 0; conf[i].data_type != -1; i++)
2171   {
2172     int default_value = conf[i].default_value;
2173     int data_type = conf[i].data_type;
2174     int conf_type = conf[i].conf_type;
2175     int byte_mask = conf_type & CONF_MASK_BYTES;
2176
2177     if (byte_mask == CONF_MASK_MULTI_BYTES)
2178     {
2179       int default_num_entities = conf[i].default_num_entities;
2180       int max_num_entities = conf[i].max_num_entities;
2181
2182       *(int *)(conf[i].num_entities) = default_num_entities;
2183
2184       if (data_type == TYPE_STRING)
2185       {
2186         char *default_string = conf[i].default_string;
2187         char *string = (char *)(conf[i].value);
2188
2189         strncpy(string, default_string, max_num_entities);
2190       }
2191       else if (data_type == TYPE_ELEMENT_LIST)
2192       {
2193         int *element_array = (int *)(conf[i].value);
2194         int j;
2195
2196         for (j = 0; j < max_num_entities; j++)
2197           element_array[j] = default_value;
2198       }
2199       else if (data_type == TYPE_CONTENT_LIST)
2200       {
2201         struct Content *content = (struct Content *)(conf[i].value);
2202         int c, x, y;
2203
2204         for (c = 0; c < max_num_entities; c++)
2205           for (y = 0; y < 3; y++)
2206             for (x = 0; x < 3; x++)
2207               content[c].e[x][y] = default_value;
2208       }
2209     }
2210     else        // constant size configuration data (1, 2 or 4 bytes)
2211     {
2212       if (data_type == TYPE_BOOLEAN)
2213         *(boolean *)(conf[i].value) = default_value;
2214       else
2215         *(int *)    (conf[i].value) = default_value;
2216     }
2217   }
2218 }
2219
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2221 {
2222   int i;
2223
2224   for (i = 0; conf[i].data_type != -1; i++)
2225   {
2226     int data_type = conf[i].data_type;
2227     int conf_type = conf[i].conf_type;
2228     int byte_mask = conf_type & CONF_MASK_BYTES;
2229
2230     if (byte_mask == CONF_MASK_MULTI_BYTES)
2231     {
2232       int max_num_entities = conf[i].max_num_entities;
2233
2234       if (data_type == TYPE_STRING)
2235       {
2236         char *string      = (char *)(conf[i].value);
2237         char *string_copy = (char *)(conf[i].value_copy);
2238
2239         strncpy(string_copy, string, max_num_entities);
2240       }
2241       else if (data_type == TYPE_ELEMENT_LIST)
2242       {
2243         int *element_array      = (int *)(conf[i].value);
2244         int *element_array_copy = (int *)(conf[i].value_copy);
2245         int j;
2246
2247         for (j = 0; j < max_num_entities; j++)
2248           element_array_copy[j] = element_array[j];
2249       }
2250       else if (data_type == TYPE_CONTENT_LIST)
2251       {
2252         struct Content *content      = (struct Content *)(conf[i].value);
2253         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2254         int c, x, y;
2255
2256         for (c = 0; c < max_num_entities; c++)
2257           for (y = 0; y < 3; y++)
2258             for (x = 0; x < 3; x++)
2259               content_copy[c].e[x][y] = content[c].e[x][y];
2260       }
2261     }
2262     else        // constant size configuration data (1, 2 or 4 bytes)
2263     {
2264       if (data_type == TYPE_BOOLEAN)
2265         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2266       else
2267         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2268     }
2269   }
2270 }
2271
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2273 {
2274   int i;
2275
2276   xx_ei = *ei_from;     // copy element data into temporary buffer
2277   yy_ei = *ei_to;       // copy element data into temporary buffer
2278
2279   copyConfigFromConfigList(chunk_config_CUSX_base);
2280
2281   *ei_from = xx_ei;
2282   *ei_to   = yy_ei;
2283
2284   // ---------- reinitialize and copy change pages ----------
2285
2286   ei_to->num_change_pages = ei_from->num_change_pages;
2287   ei_to->current_change_page = ei_from->current_change_page;
2288
2289   setElementChangePages(ei_to, ei_to->num_change_pages);
2290
2291   for (i = 0; i < ei_to->num_change_pages; i++)
2292     ei_to->change_page[i] = ei_from->change_page[i];
2293
2294   // ---------- copy group element info ----------
2295   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2296     *ei_to->group = *ei_from->group;
2297
2298   // mark this custom element as modified
2299   ei_to->modified_settings = TRUE;
2300 }
2301
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2303 {
2304   int change_page_size = sizeof(struct ElementChangeInfo);
2305
2306   ei->num_change_pages = MAX(1, change_pages);
2307
2308   ei->change_page =
2309     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2310
2311   if (ei->current_change_page >= ei->num_change_pages)
2312     ei->current_change_page = ei->num_change_pages - 1;
2313
2314   ei->change = &ei->change_page[ei->current_change_page];
2315 }
2316
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2318 {
2319   xx_change = *change;          // copy change data into temporary buffer
2320
2321   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2322
2323   *change = xx_change;
2324
2325   resetEventFlags(change);
2326
2327   change->direct_action = 0;
2328   change->other_action = 0;
2329
2330   change->pre_change_function = NULL;
2331   change->change_function = NULL;
2332   change->post_change_function = NULL;
2333 }
2334
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2336 {
2337   int i, x, y;
2338
2339   li = *level;          // copy level data into temporary buffer
2340   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2341   *level = li;          // copy temporary buffer back to level data
2342
2343   setLevelInfoToDefaults_BD();
2344   setLevelInfoToDefaults_EM();
2345   setLevelInfoToDefaults_SP();
2346   setLevelInfoToDefaults_MM();
2347
2348   level->native_bd_level = &native_bd_level;
2349   level->native_em_level = &native_em_level;
2350   level->native_sp_level = &native_sp_level;
2351   level->native_mm_level = &native_mm_level;
2352
2353   level->file_version = FILE_VERSION_ACTUAL;
2354   level->game_version = GAME_VERSION_ACTUAL;
2355
2356   level->creation_date = getCurrentDate();
2357
2358   level->encoding_16bit_field  = TRUE;
2359   level->encoding_16bit_yamyam = TRUE;
2360   level->encoding_16bit_amoeba = TRUE;
2361
2362   // clear level name and level author string buffers
2363   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2364     level->name[i] = '\0';
2365   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2366     level->author[i] = '\0';
2367
2368   // set level name and level author to default values
2369   strcpy(level->name, NAMELESS_LEVEL_NAME);
2370   strcpy(level->author, ANONYMOUS_NAME);
2371
2372   // set level playfield to playable default level with player and exit
2373   for (x = 0; x < MAX_LEV_FIELDX; x++)
2374     for (y = 0; y < MAX_LEV_FIELDY; y++)
2375       level->field[x][y] = EL_SAND;
2376
2377   level->field[0][0] = EL_PLAYER_1;
2378   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2379
2380   BorderElement = EL_STEELWALL;
2381
2382   // detect custom elements when loading them
2383   level->file_has_custom_elements = FALSE;
2384
2385   // set default color type and colors for BD style level colors
2386   SetDefaultLevelColorType_BD();
2387   SetDefaultLevelColors_BD();
2388
2389   // set all bug compatibility flags to "false" => do not emulate this bug
2390   level->use_action_after_change_bug = FALSE;
2391
2392   if (leveldir_current)
2393   {
2394     // try to determine better author name than 'anonymous'
2395     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2396     {
2397       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2398       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2399     }
2400     else
2401     {
2402       switch (LEVELCLASS(leveldir_current))
2403       {
2404         case LEVELCLASS_TUTORIAL:
2405           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2406           break;
2407
2408         case LEVELCLASS_CONTRIB:
2409           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2410           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2411           break;
2412
2413         case LEVELCLASS_PRIVATE:
2414           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2415           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2416           break;
2417
2418         default:
2419           // keep default value
2420           break;
2421       }
2422     }
2423   }
2424 }
2425
2426 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2427 {
2428   static boolean clipboard_elements_initialized = FALSE;
2429   int i;
2430
2431   InitElementPropertiesStatic();
2432
2433   li = *level;          // copy level data into temporary buffer
2434   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2435   *level = li;          // copy temporary buffer back to level data
2436
2437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2438   {
2439     int element = i;
2440     struct ElementInfo *ei = &element_info[element];
2441
2442     if (element == EL_MM_GRAY_BALL)
2443     {
2444       struct LevelInfo_MM *level_mm = level->native_mm_level;
2445       int j;
2446
2447       for (j = 0; j < level->num_mm_ball_contents; j++)
2448         level->mm_ball_content[j] =
2449           map_element_MM_to_RND(level_mm->ball_content[j]);
2450     }
2451
2452     // never initialize clipboard elements after the very first time
2453     // (to be able to use clipboard elements between several levels)
2454     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2455       continue;
2456
2457     if (IS_ENVELOPE(element))
2458     {
2459       int envelope_nr = element - EL_ENVELOPE_1;
2460
2461       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2462
2463       level->envelope[envelope_nr] = xx_envelope;
2464     }
2465
2466     if (IS_CUSTOM_ELEMENT(element) ||
2467         IS_GROUP_ELEMENT(element) ||
2468         IS_INTERNAL_ELEMENT(element))
2469     {
2470       xx_ei = *ei;      // copy element data into temporary buffer
2471
2472       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2473
2474       *ei = xx_ei;
2475     }
2476
2477     setElementChangePages(ei, 1);
2478     setElementChangeInfoToDefaults(ei->change);
2479
2480     if (IS_CUSTOM_ELEMENT(element) ||
2481         IS_GROUP_ELEMENT(element))
2482     {
2483       setElementDescriptionToDefault(ei);
2484
2485       ei->modified_settings = FALSE;
2486     }
2487
2488     if (IS_CUSTOM_ELEMENT(element) ||
2489         IS_INTERNAL_ELEMENT(element))
2490     {
2491       // internal values used in level editor
2492
2493       ei->access_type = 0;
2494       ei->access_layer = 0;
2495       ei->access_protected = 0;
2496       ei->walk_to_action = 0;
2497       ei->smash_targets = 0;
2498       ei->deadliness = 0;
2499
2500       ei->can_explode_by_fire = FALSE;
2501       ei->can_explode_smashed = FALSE;
2502       ei->can_explode_impact = FALSE;
2503
2504       ei->current_change_page = 0;
2505     }
2506
2507     if (IS_GROUP_ELEMENT(element) ||
2508         IS_INTERNAL_ELEMENT(element))
2509     {
2510       struct ElementGroupInfo *group;
2511
2512       // initialize memory for list of elements in group
2513       if (ei->group == NULL)
2514         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2515
2516       group = ei->group;
2517
2518       xx_group = *group;        // copy group data into temporary buffer
2519
2520       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2521
2522       *group = xx_group;
2523     }
2524
2525     if (IS_EMPTY_ELEMENT(element) ||
2526         IS_INTERNAL_ELEMENT(element))
2527     {
2528       xx_ei = *ei;              // copy element data into temporary buffer
2529
2530       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2531
2532       *ei = xx_ei;
2533     }
2534   }
2535
2536   clipboard_elements_initialized = TRUE;
2537 }
2538
2539 static void setLevelInfoToDefaults(struct LevelInfo *level,
2540                                    boolean level_info_only,
2541                                    boolean reset_file_status)
2542 {
2543   setLevelInfoToDefaults_Level(level);
2544
2545   if (!level_info_only)
2546     setLevelInfoToDefaults_Elements(level);
2547
2548   if (reset_file_status)
2549   {
2550     level->no_valid_file = FALSE;
2551     level->no_level_file = FALSE;
2552   }
2553
2554   level->changed = FALSE;
2555 }
2556
2557 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2558 {
2559   level_file_info->nr = 0;
2560   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2561   level_file_info->packed = FALSE;
2562
2563   setString(&level_file_info->basename, NULL);
2564   setString(&level_file_info->filename, NULL);
2565 }
2566
2567 int getMappedElement_SB(int, boolean);
2568
2569 static void ActivateLevelTemplate(void)
2570 {
2571   int x, y;
2572
2573   if (check_special_flags("load_xsb_to_ces"))
2574   {
2575     // fill smaller playfields with padding "beyond border wall" elements
2576     if (level.fieldx < level_template.fieldx ||
2577         level.fieldy < level_template.fieldy)
2578     {
2579       short field[level.fieldx][level.fieldy];
2580       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2581       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2582       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2583       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2584
2585       // copy old playfield (which is smaller than the visible area)
2586       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2587         field[x][y] = level.field[x][y];
2588
2589       // fill new, larger playfield with "beyond border wall" elements
2590       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2591         level.field[x][y] = getMappedElement_SB('_', TRUE);
2592
2593       // copy the old playfield to the middle of the new playfield
2594       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2595         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2596
2597       level.fieldx = new_fieldx;
2598       level.fieldy = new_fieldy;
2599     }
2600   }
2601
2602   // Currently there is no special action needed to activate the template
2603   // data, because 'element_info' property settings overwrite the original
2604   // level data, while all other variables do not change.
2605
2606   // Exception: 'from_level_template' elements in the original level playfield
2607   // are overwritten with the corresponding elements at the same position in
2608   // playfield from the level template.
2609
2610   for (x = 0; x < level.fieldx; x++)
2611     for (y = 0; y < level.fieldy; y++)
2612       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2613         level.field[x][y] = level_template.field[x][y];
2614
2615   if (check_special_flags("load_xsb_to_ces"))
2616   {
2617     struct LevelInfo level_backup = level;
2618
2619     // overwrite all individual level settings from template level settings
2620     level = level_template;
2621
2622     // restore level file info
2623     level.file_info = level_backup.file_info;
2624
2625     // restore playfield size
2626     level.fieldx = level_backup.fieldx;
2627     level.fieldy = level_backup.fieldy;
2628
2629     // restore playfield content
2630     for (x = 0; x < level.fieldx; x++)
2631       for (y = 0; y < level.fieldy; y++)
2632         level.field[x][y] = level_backup.field[x][y];
2633
2634     // restore name and author from individual level
2635     strcpy(level.name,   level_backup.name);
2636     strcpy(level.author, level_backup.author);
2637
2638     // restore flag "use_custom_template"
2639     level.use_custom_template = level_backup.use_custom_template;
2640   }
2641 }
2642
2643 static boolean checkForPackageFromBasename_BD(char *basename)
2644 {
2645   // check for native BD level file extensions
2646   if (!strSuffixLower(basename, ".bd") &&
2647       !strSuffixLower(basename, ".bdr") &&
2648       !strSuffixLower(basename, ".brc") &&
2649       !strSuffixLower(basename, ".gds"))
2650     return FALSE;
2651
2652   // check for standard single-level BD files (like "001.bd")
2653   if (strSuffixLower(basename, ".bd") &&
2654       strlen(basename) == 6 &&
2655       basename[0] >= '0' && basename[0] <= '9' &&
2656       basename[1] >= '0' && basename[1] <= '9' &&
2657       basename[2] >= '0' && basename[2] <= '9')
2658     return FALSE;
2659
2660   // this is a level package in native BD file format
2661   return TRUE;
2662 }
2663
2664 static char *getLevelFilenameFromBasename(char *basename)
2665 {
2666   static char *filename = NULL;
2667
2668   checked_free(filename);
2669
2670   filename = getPath2(getCurrentLevelDir(), basename);
2671
2672   return filename;
2673 }
2674
2675 static int getFileTypeFromBasename(char *basename)
2676 {
2677   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2678
2679   static char *filename = NULL;
2680   struct stat file_status;
2681
2682   // ---------- try to determine file type from filename ----------
2683
2684   // check for typical filename of a Supaplex level package file
2685   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2686     return LEVEL_FILE_TYPE_SP;
2687
2688   // check for typical filename of a Diamond Caves II level package file
2689   if (strSuffixLower(basename, ".dc") ||
2690       strSuffixLower(basename, ".dc2"))
2691     return LEVEL_FILE_TYPE_DC;
2692
2693   // check for typical filename of a Sokoban level package file
2694   if (strSuffixLower(basename, ".xsb") &&
2695       strchr(basename, '%') == NULL)
2696     return LEVEL_FILE_TYPE_SB;
2697
2698   // check for typical filename of a Boulder Dash (GDash) level package file
2699   if (checkForPackageFromBasename_BD(basename))
2700     return LEVEL_FILE_TYPE_BD;
2701
2702   // ---------- try to determine file type from filesize ----------
2703
2704   checked_free(filename);
2705   filename = getPath2(getCurrentLevelDir(), basename);
2706
2707   if (stat(filename, &file_status) == 0)
2708   {
2709     // check for typical filesize of a Supaplex level package file
2710     if (file_status.st_size == 170496)
2711       return LEVEL_FILE_TYPE_SP;
2712   }
2713
2714   return LEVEL_FILE_TYPE_UNKNOWN;
2715 }
2716
2717 static int getFileTypeFromMagicBytes(char *filename, int type)
2718 {
2719   File *file;
2720
2721   if ((file = openFile(filename, MODE_READ)))
2722   {
2723     char chunk_name[CHUNK_ID_LEN + 1];
2724
2725     getFileChunkBE(file, chunk_name, NULL);
2726
2727     if (strEqual(chunk_name, "MMII") ||
2728         strEqual(chunk_name, "MIRR"))
2729       type = LEVEL_FILE_TYPE_MM;
2730
2731     closeFile(file);
2732   }
2733
2734   return type;
2735 }
2736
2737 static boolean checkForPackageFromBasename(char *basename)
2738 {
2739   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2740   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2741
2742   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2743 }
2744
2745 static char *getSingleLevelBasenameExt(int nr, char *extension)
2746 {
2747   static char basename[MAX_FILENAME_LEN];
2748
2749   if (nr < 0)
2750     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2751   else
2752     sprintf(basename, "%03d.%s", nr, extension);
2753
2754   return basename;
2755 }
2756
2757 static char *getSingleLevelBasename(int nr)
2758 {
2759   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2760 }
2761
2762 static char *getPackedLevelBasename(int type)
2763 {
2764   static char basename[MAX_FILENAME_LEN];
2765   char *directory = getCurrentLevelDir();
2766   Directory *dir;
2767   DirectoryEntry *dir_entry;
2768
2769   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2770
2771   if ((dir = openDirectory(directory)) == NULL)
2772   {
2773     Warn("cannot read current level directory '%s'", directory);
2774
2775     return basename;
2776   }
2777
2778   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2779   {
2780     char *entry_basename = dir_entry->basename;
2781     int entry_type = getFileTypeFromBasename(entry_basename);
2782
2783     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2784     {
2785       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2786           type == entry_type)
2787       {
2788         strcpy(basename, entry_basename);
2789
2790         break;
2791       }
2792     }
2793   }
2794
2795   closeDirectory(dir);
2796
2797   return basename;
2798 }
2799
2800 static char *getSingleLevelFilename(int nr)
2801 {
2802   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2803 }
2804
2805 #if ENABLE_UNUSED_CODE
2806 static char *getPackedLevelFilename(int type)
2807 {
2808   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2809 }
2810 #endif
2811
2812 char *getDefaultLevelFilename(int nr)
2813 {
2814   return getSingleLevelFilename(nr);
2815 }
2816
2817 #if ENABLE_UNUSED_CODE
2818 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2819                                                  int type)
2820 {
2821   lfi->type = type;
2822   lfi->packed = FALSE;
2823
2824   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2825   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2826 }
2827 #endif
2828
2829 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2830                                                  int type, char *format, ...)
2831 {
2832   static char basename[MAX_FILENAME_LEN];
2833   va_list ap;
2834
2835   va_start(ap, format);
2836   vsprintf(basename, format, ap);
2837   va_end(ap);
2838
2839   lfi->type = type;
2840   lfi->packed = FALSE;
2841
2842   setString(&lfi->basename, basename);
2843   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2844 }
2845
2846 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2847                                                  int type)
2848 {
2849   lfi->type = type;
2850   lfi->packed = TRUE;
2851
2852   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2853   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2854 }
2855
2856 static int getFiletypeFromID(char *filetype_id)
2857 {
2858   char *filetype_id_lower;
2859   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2860   int i;
2861
2862   if (filetype_id == NULL)
2863     return LEVEL_FILE_TYPE_UNKNOWN;
2864
2865   filetype_id_lower = getStringToLower(filetype_id);
2866
2867   for (i = 0; filetype_id_list[i].id != NULL; i++)
2868   {
2869     char *id_lower = getStringToLower(filetype_id_list[i].id);
2870     
2871     if (strEqual(filetype_id_lower, id_lower))
2872       filetype = filetype_id_list[i].filetype;
2873
2874     free(id_lower);
2875
2876     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2877       break;
2878   }
2879
2880   free(filetype_id_lower);
2881
2882   return filetype;
2883 }
2884
2885 char *getLocalLevelTemplateFilename(void)
2886 {
2887   return getDefaultLevelFilename(-1);
2888 }
2889
2890 char *getGlobalLevelTemplateFilename(void)
2891 {
2892   // global variable "leveldir_current" must be modified in the loop below
2893   LevelDirTree *leveldir_current_last = leveldir_current;
2894   char *filename = NULL;
2895
2896   // check for template level in path from current to topmost tree node
2897
2898   while (leveldir_current != NULL)
2899   {
2900     filename = getDefaultLevelFilename(-1);
2901
2902     if (fileExists(filename))
2903       break;
2904
2905     leveldir_current = leveldir_current->node_parent;
2906   }
2907
2908   // restore global variable "leveldir_current" modified in above loop
2909   leveldir_current = leveldir_current_last;
2910
2911   return filename;
2912 }
2913
2914 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2915 {
2916   int nr = lfi->nr;
2917
2918   // special case: level number is negative => check for level template file
2919   if (nr < 0)
2920   {
2921     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2922                                          getSingleLevelBasename(-1));
2923
2924     // replace local level template filename with global template filename
2925     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2926
2927     // no fallback if template file not existing
2928     return;
2929   }
2930
2931   // special case: check for file name/pattern specified in "levelinfo.conf"
2932   if (leveldir_current->level_filename != NULL)
2933   {
2934     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2935
2936     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2937                                          leveldir_current->level_filename, nr);
2938
2939     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2940
2941     if (fileExists(lfi->filename))
2942       return;
2943   }
2944   else if (leveldir_current->level_filetype != NULL)
2945   {
2946     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2947
2948     // check for specified native level file with standard file name
2949     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2950                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2951     if (fileExists(lfi->filename))
2952       return;
2953   }
2954
2955   // check for native Rocks'n'Diamonds level file
2956   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2957                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2958   if (fileExists(lfi->filename))
2959     return;
2960
2961   // check for native Boulder Dash level file
2962   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2963   if (fileExists(lfi->filename))
2964     return;
2965
2966   // check for Emerald Mine level file (V1)
2967   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2968                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2969   if (fileExists(lfi->filename))
2970     return;
2971   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2972                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2973   if (fileExists(lfi->filename))
2974     return;
2975
2976   // check for Emerald Mine level file (V2 to V5)
2977   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2978   if (fileExists(lfi->filename))
2979     return;
2980
2981   // check for Emerald Mine level file (V6 / single mode)
2982   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2983   if (fileExists(lfi->filename))
2984     return;
2985   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2986   if (fileExists(lfi->filename))
2987     return;
2988
2989   // check for Emerald Mine level file (V6 / teamwork mode)
2990   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2991   if (fileExists(lfi->filename))
2992     return;
2993   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2994   if (fileExists(lfi->filename))
2995     return;
2996
2997   // check for various packed level file formats
2998   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2999   if (fileExists(lfi->filename))
3000     return;
3001
3002   // no known level file found -- use default values (and fail later)
3003   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3004                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3005 }
3006
3007 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3008 {
3009   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3010     lfi->type = getFileTypeFromBasename(lfi->basename);
3011
3012   if (lfi->type == LEVEL_FILE_TYPE_RND)
3013     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3014 }
3015
3016 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3017 {
3018   // always start with reliable default values
3019   setFileInfoToDefaults(level_file_info);
3020
3021   level_file_info->nr = nr;     // set requested level number
3022
3023   determineLevelFileInfo_Filename(level_file_info);
3024   determineLevelFileInfo_Filetype(level_file_info);
3025 }
3026
3027 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3028                               struct LevelFileInfo *lfi_to)
3029 {
3030   lfi_to->nr     = lfi_from->nr;
3031   lfi_to->type   = lfi_from->type;
3032   lfi_to->packed = lfi_from->packed;
3033
3034   setString(&lfi_to->basename, lfi_from->basename);
3035   setString(&lfi_to->filename, lfi_from->filename);
3036 }
3037
3038 // ----------------------------------------------------------------------------
3039 // functions for loading R'n'D level
3040 // ----------------------------------------------------------------------------
3041
3042 int getMappedElement(int element)
3043 {
3044   // remap some (historic, now obsolete) elements
3045
3046   switch (element)
3047   {
3048     case EL_PLAYER_OBSOLETE:
3049       element = EL_PLAYER_1;
3050       break;
3051
3052     case EL_KEY_OBSOLETE:
3053       element = EL_KEY_1;
3054       break;
3055
3056     case EL_EM_KEY_1_FILE_OBSOLETE:
3057       element = EL_EM_KEY_1;
3058       break;
3059
3060     case EL_EM_KEY_2_FILE_OBSOLETE:
3061       element = EL_EM_KEY_2;
3062       break;
3063
3064     case EL_EM_KEY_3_FILE_OBSOLETE:
3065       element = EL_EM_KEY_3;
3066       break;
3067
3068     case EL_EM_KEY_4_FILE_OBSOLETE:
3069       element = EL_EM_KEY_4;
3070       break;
3071
3072     case EL_ENVELOPE_OBSOLETE:
3073       element = EL_ENVELOPE_1;
3074       break;
3075
3076     case EL_SP_EMPTY:
3077       element = EL_EMPTY;
3078       break;
3079
3080     default:
3081       if (element >= NUM_FILE_ELEMENTS)
3082       {
3083         Warn("invalid level element %d", element);
3084
3085         element = EL_UNKNOWN;
3086       }
3087       break;
3088   }
3089
3090   return element;
3091 }
3092
3093 static int getMappedElementByVersion(int element, int game_version)
3094 {
3095   // remap some elements due to certain game version
3096
3097   if (game_version <= VERSION_IDENT(2,2,0,0))
3098   {
3099     // map game font elements
3100     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3101                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3102                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3103                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3104   }
3105
3106   if (game_version < VERSION_IDENT(3,0,0,0))
3107   {
3108     // map Supaplex gravity tube elements
3109     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3110                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3111                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3112                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3113                element);
3114   }
3115
3116   return element;
3117 }
3118
3119 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3120 {
3121   level->file_version = getFileVersion(file);
3122   level->game_version = getFileVersion(file);
3123
3124   return chunk_size;
3125 }
3126
3127 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3128 {
3129   level->creation_date.year  = getFile16BitBE(file);
3130   level->creation_date.month = getFile8Bit(file);
3131   level->creation_date.day   = getFile8Bit(file);
3132
3133   level->creation_date.src   = DATE_SRC_LEVELFILE;
3134
3135   return chunk_size;
3136 }
3137
3138 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3139 {
3140   int initial_player_stepsize;
3141   int initial_player_gravity;
3142   int i, x, y;
3143
3144   level->fieldx = getFile8Bit(file);
3145   level->fieldy = getFile8Bit(file);
3146
3147   level->time           = getFile16BitBE(file);
3148   level->gems_needed    = getFile16BitBE(file);
3149
3150   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3151     level->name[i] = getFile8Bit(file);
3152   level->name[MAX_LEVEL_NAME_LEN] = 0;
3153
3154   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3155     level->score[i] = getFile8Bit(file);
3156
3157   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3158   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3159     for (y = 0; y < 3; y++)
3160       for (x = 0; x < 3; x++)
3161         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3162
3163   level->amoeba_speed           = getFile8Bit(file);
3164   level->time_magic_wall        = getFile8Bit(file);
3165   level->time_wheel             = getFile8Bit(file);
3166   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3167
3168   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3169                                    STEPSIZE_NORMAL);
3170
3171   for (i = 0; i < MAX_PLAYERS; i++)
3172     level->initial_player_stepsize[i] = initial_player_stepsize;
3173
3174   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3175
3176   for (i = 0; i < MAX_PLAYERS; i++)
3177     level->initial_player_gravity[i] = initial_player_gravity;
3178
3179   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3180   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3181
3182   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3183
3184   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3185   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3186   level->can_move_into_acid_bits = getFile32BitBE(file);
3187   level->dont_collide_with_bits = getFile8Bit(file);
3188
3189   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3190   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3191
3192   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3193   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3194   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3195
3196   level->game_engine_type       = getFile8Bit(file);
3197
3198   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3199
3200   return chunk_size;
3201 }
3202
3203 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3204 {
3205   int i;
3206
3207   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3208     level->name[i] = getFile8Bit(file);
3209   level->name[MAX_LEVEL_NAME_LEN] = 0;
3210
3211   return chunk_size;
3212 }
3213
3214 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3215 {
3216   int i;
3217
3218   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3219     level->author[i] = getFile8Bit(file);
3220   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3221
3222   return chunk_size;
3223 }
3224
3225 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3226 {
3227   int x, y;
3228   int chunk_size_expected = level->fieldx * level->fieldy;
3229
3230   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3231      stored with 16-bit encoding (and should be twice as big then).
3232      Even worse, playfield data was stored 16-bit when only yamyam content
3233      contained 16-bit elements and vice versa. */
3234
3235   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3236     chunk_size_expected *= 2;
3237
3238   if (chunk_size_expected != chunk_size)
3239   {
3240     ReadUnusedBytesFromFile(file, chunk_size);
3241     return chunk_size_expected;
3242   }
3243
3244   for (y = 0; y < level->fieldy; y++)
3245     for (x = 0; x < level->fieldx; x++)
3246       level->field[x][y] =
3247         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3248                          getFile8Bit(file));
3249   return chunk_size;
3250 }
3251
3252 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3253 {
3254   int i, x, y;
3255   int header_size = 4;
3256   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3257   int chunk_size_expected = header_size + content_size;
3258
3259   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3260      stored with 16-bit encoding (and should be twice as big then).
3261      Even worse, playfield data was stored 16-bit when only yamyam content
3262      contained 16-bit elements and vice versa. */
3263
3264   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3265     chunk_size_expected += content_size;
3266
3267   if (chunk_size_expected != chunk_size)
3268   {
3269     ReadUnusedBytesFromFile(file, chunk_size);
3270     return chunk_size_expected;
3271   }
3272
3273   getFile8Bit(file);
3274   level->num_yamyam_contents = getFile8Bit(file);
3275   getFile8Bit(file);
3276   getFile8Bit(file);
3277
3278   // correct invalid number of content fields -- should never happen
3279   if (level->num_yamyam_contents < 1 ||
3280       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3281     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3282
3283   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3284     for (y = 0; y < 3; y++)
3285       for (x = 0; x < 3; x++)
3286         level->yamyam_content[i].e[x][y] =
3287           getMappedElement(level->encoding_16bit_field ?
3288                            getFile16BitBE(file) : getFile8Bit(file));
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i, x, y;
3295   int element;
3296   int num_contents;
3297   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3298
3299   element = getMappedElement(getFile16BitBE(file));
3300   num_contents = getFile8Bit(file);
3301
3302   getFile8Bit(file);    // content x size (unused)
3303   getFile8Bit(file);    // content y size (unused)
3304
3305   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3306
3307   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3308     for (y = 0; y < 3; y++)
3309       for (x = 0; x < 3; x++)
3310         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3311
3312   // correct invalid number of content fields -- should never happen
3313   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3314     num_contents = STD_ELEMENT_CONTENTS;
3315
3316   if (element == EL_YAMYAM)
3317   {
3318     level->num_yamyam_contents = num_contents;
3319
3320     for (i = 0; i < num_contents; i++)
3321       for (y = 0; y < 3; y++)
3322         for (x = 0; x < 3; x++)
3323           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3324   }
3325   else if (element == EL_BD_AMOEBA)
3326   {
3327     level->amoeba_content = content_array[0][0][0];
3328   }
3329   else
3330   {
3331     Warn("cannot load content for element '%d'", element);
3332   }
3333
3334   return chunk_size;
3335 }
3336
3337 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3338 {
3339   int i;
3340   int element;
3341   int envelope_nr;
3342   int envelope_len;
3343   int chunk_size_expected;
3344
3345   element = getMappedElement(getFile16BitBE(file));
3346   if (!IS_ENVELOPE(element))
3347     element = EL_ENVELOPE_1;
3348
3349   envelope_nr = element - EL_ENVELOPE_1;
3350
3351   envelope_len = getFile16BitBE(file);
3352
3353   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3354   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3355
3356   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3357
3358   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3359   if (chunk_size_expected != chunk_size)
3360   {
3361     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3362     return chunk_size_expected;
3363   }
3364
3365   for (i = 0; i < envelope_len; i++)
3366     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3367
3368   return chunk_size;
3369 }
3370
3371 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3372 {
3373   int num_changed_custom_elements = getFile16BitBE(file);
3374   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3375   int i;
3376
3377   if (chunk_size_expected != chunk_size)
3378   {
3379     ReadUnusedBytesFromFile(file, chunk_size - 2);
3380     return chunk_size_expected;
3381   }
3382
3383   for (i = 0; i < num_changed_custom_elements; i++)
3384   {
3385     int element = getMappedElement(getFile16BitBE(file));
3386     int properties = getFile32BitBE(file);
3387
3388     if (IS_CUSTOM_ELEMENT(element))
3389       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3390     else
3391       Warn("invalid custom element number %d", element);
3392
3393     // older game versions that wrote level files with CUS1 chunks used
3394     // different default push delay values (not yet stored in level file)
3395     element_info[element].push_delay_fixed = 2;
3396     element_info[element].push_delay_random = 8;
3397   }
3398
3399   level->file_has_custom_elements = TRUE;
3400
3401   return chunk_size;
3402 }
3403
3404 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3405 {
3406   int num_changed_custom_elements = getFile16BitBE(file);
3407   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3408   int i;
3409
3410   if (chunk_size_expected != chunk_size)
3411   {
3412     ReadUnusedBytesFromFile(file, chunk_size - 2);
3413     return chunk_size_expected;
3414   }
3415
3416   for (i = 0; i < num_changed_custom_elements; i++)
3417   {
3418     int element = getMappedElement(getFile16BitBE(file));
3419     int custom_target_element = getMappedElement(getFile16BitBE(file));
3420
3421     if (IS_CUSTOM_ELEMENT(element))
3422       element_info[element].change->target_element = custom_target_element;
3423     else
3424       Warn("invalid custom element number %d", element);
3425   }
3426
3427   level->file_has_custom_elements = TRUE;
3428
3429   return chunk_size;
3430 }
3431
3432 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3433 {
3434   int num_changed_custom_elements = getFile16BitBE(file);
3435   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3436   int i, j, x, y;
3437
3438   if (chunk_size_expected != chunk_size)
3439   {
3440     ReadUnusedBytesFromFile(file, chunk_size - 2);
3441     return chunk_size_expected;
3442   }
3443
3444   for (i = 0; i < num_changed_custom_elements; i++)
3445   {
3446     int element = getMappedElement(getFile16BitBE(file));
3447     struct ElementInfo *ei = &element_info[element];
3448     unsigned int event_bits;
3449
3450     if (!IS_CUSTOM_ELEMENT(element))
3451     {
3452       Warn("invalid custom element number %d", element);
3453
3454       element = EL_INTERNAL_DUMMY;
3455     }
3456
3457     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3458       ei->description[j] = getFile8Bit(file);
3459     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3460
3461     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3462
3463     // some free bytes for future properties and padding
3464     ReadUnusedBytesFromFile(file, 7);
3465
3466     ei->use_gfx_element = getFile8Bit(file);
3467     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3468
3469     ei->collect_score_initial = getFile8Bit(file);
3470     ei->collect_count_initial = getFile8Bit(file);
3471
3472     ei->push_delay_fixed = getFile16BitBE(file);
3473     ei->push_delay_random = getFile16BitBE(file);
3474     ei->move_delay_fixed = getFile16BitBE(file);
3475     ei->move_delay_random = getFile16BitBE(file);
3476
3477     ei->move_pattern = getFile16BitBE(file);
3478     ei->move_direction_initial = getFile8Bit(file);
3479     ei->move_stepsize = getFile8Bit(file);
3480
3481     for (y = 0; y < 3; y++)
3482       for (x = 0; x < 3; x++)
3483         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3484
3485     // bits 0 - 31 of "has_event[]"
3486     event_bits = getFile32BitBE(file);
3487     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3488       if (event_bits & (1u << j))
3489         ei->change->has_event[j] = TRUE;
3490
3491     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3492
3493     ei->change->delay_fixed = getFile16BitBE(file);
3494     ei->change->delay_random = getFile16BitBE(file);
3495     ei->change->delay_frames = getFile16BitBE(file);
3496
3497     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3498
3499     ei->change->explode = getFile8Bit(file);
3500     ei->change->use_target_content = getFile8Bit(file);
3501     ei->change->only_if_complete = getFile8Bit(file);
3502     ei->change->use_random_replace = getFile8Bit(file);
3503
3504     ei->change->random_percentage = getFile8Bit(file);
3505     ei->change->replace_when = getFile8Bit(file);
3506
3507     for (y = 0; y < 3; y++)
3508       for (x = 0; x < 3; x++)
3509         ei->change->target_content.e[x][y] =
3510           getMappedElement(getFile16BitBE(file));
3511
3512     ei->slippery_type = getFile8Bit(file);
3513
3514     // some free bytes for future properties and padding
3515     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3516
3517     // mark that this custom element has been modified
3518     ei->modified_settings = TRUE;
3519   }
3520
3521   level->file_has_custom_elements = TRUE;
3522
3523   return chunk_size;
3524 }
3525
3526 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3527 {
3528   struct ElementInfo *ei;
3529   int chunk_size_expected;
3530   int element;
3531   int i, j, x, y;
3532
3533   // ---------- custom element base property values (96 bytes) ----------------
3534
3535   element = getMappedElement(getFile16BitBE(file));
3536
3537   if (!IS_CUSTOM_ELEMENT(element))
3538   {
3539     Warn("invalid custom element number %d", element);
3540
3541     ReadUnusedBytesFromFile(file, chunk_size - 2);
3542
3543     return chunk_size;
3544   }
3545
3546   ei = &element_info[element];
3547
3548   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3549     ei->description[i] = getFile8Bit(file);
3550   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3551
3552   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3553
3554   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3555
3556   ei->num_change_pages = getFile8Bit(file);
3557
3558   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3559   if (chunk_size_expected != chunk_size)
3560   {
3561     ReadUnusedBytesFromFile(file, chunk_size - 43);
3562     return chunk_size_expected;
3563   }
3564
3565   ei->ce_value_fixed_initial = getFile16BitBE(file);
3566   ei->ce_value_random_initial = getFile16BitBE(file);
3567   ei->use_last_ce_value = getFile8Bit(file);
3568
3569   ei->use_gfx_element = getFile8Bit(file);
3570   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3571
3572   ei->collect_score_initial = getFile8Bit(file);
3573   ei->collect_count_initial = getFile8Bit(file);
3574
3575   ei->drop_delay_fixed = getFile8Bit(file);
3576   ei->push_delay_fixed = getFile8Bit(file);
3577   ei->drop_delay_random = getFile8Bit(file);
3578   ei->push_delay_random = getFile8Bit(file);
3579   ei->move_delay_fixed = getFile16BitBE(file);
3580   ei->move_delay_random = getFile16BitBE(file);
3581
3582   // bits 0 - 15 of "move_pattern" ...
3583   ei->move_pattern = getFile16BitBE(file);
3584   ei->move_direction_initial = getFile8Bit(file);
3585   ei->move_stepsize = getFile8Bit(file);
3586
3587   ei->slippery_type = getFile8Bit(file);
3588
3589   for (y = 0; y < 3; y++)
3590     for (x = 0; x < 3; x++)
3591       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3592
3593   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3594   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3595   ei->move_leave_type = getFile8Bit(file);
3596
3597   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3598   ei->move_pattern |= (getFile16BitBE(file) << 16);
3599
3600   ei->access_direction = getFile8Bit(file);
3601
3602   ei->explosion_delay = getFile8Bit(file);
3603   ei->ignition_delay = getFile8Bit(file);
3604   ei->explosion_type = getFile8Bit(file);
3605
3606   // some free bytes for future custom property values and padding
3607   ReadUnusedBytesFromFile(file, 1);
3608
3609   // ---------- change page property values (48 bytes) ------------------------
3610
3611   setElementChangePages(ei, ei->num_change_pages);
3612
3613   for (i = 0; i < ei->num_change_pages; i++)
3614   {
3615     struct ElementChangeInfo *change = &ei->change_page[i];
3616     unsigned int event_bits;
3617
3618     // always start with reliable default values
3619     setElementChangeInfoToDefaults(change);
3620
3621     // bits 0 - 31 of "has_event[]" ...
3622     event_bits = getFile32BitBE(file);
3623     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3624       if (event_bits & (1u << j))
3625         change->has_event[j] = TRUE;
3626
3627     change->target_element = getMappedElement(getFile16BitBE(file));
3628
3629     change->delay_fixed = getFile16BitBE(file);
3630     change->delay_random = getFile16BitBE(file);
3631     change->delay_frames = getFile16BitBE(file);
3632
3633     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3634
3635     change->explode = getFile8Bit(file);
3636     change->use_target_content = getFile8Bit(file);
3637     change->only_if_complete = getFile8Bit(file);
3638     change->use_random_replace = getFile8Bit(file);
3639
3640     change->random_percentage = getFile8Bit(file);
3641     change->replace_when = getFile8Bit(file);
3642
3643     for (y = 0; y < 3; y++)
3644       for (x = 0; x < 3; x++)
3645         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3646
3647     change->can_change = getFile8Bit(file);
3648
3649     change->trigger_side = getFile8Bit(file);
3650
3651     change->trigger_player = getFile8Bit(file);
3652     change->trigger_page = getFile8Bit(file);
3653
3654     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3655                             CH_PAGE_ANY : (1 << change->trigger_page));
3656
3657     change->has_action = getFile8Bit(file);
3658     change->action_type = getFile8Bit(file);
3659     change->action_mode = getFile8Bit(file);
3660     change->action_arg = getFile16BitBE(file);
3661
3662     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3663     event_bits = getFile8Bit(file);
3664     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3665       if (event_bits & (1u << (j - 32)))
3666         change->has_event[j] = TRUE;
3667   }
3668
3669   // mark this custom element as modified
3670   ei->modified_settings = TRUE;
3671
3672   level->file_has_custom_elements = TRUE;
3673
3674   return chunk_size;
3675 }
3676
3677 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3678 {
3679   struct ElementInfo *ei;
3680   struct ElementGroupInfo *group;
3681   int element;
3682   int i;
3683
3684   element = getMappedElement(getFile16BitBE(file));
3685
3686   if (!IS_GROUP_ELEMENT(element))
3687   {
3688     Warn("invalid group element number %d", element);
3689
3690     ReadUnusedBytesFromFile(file, chunk_size - 2);
3691
3692     return chunk_size;
3693   }
3694
3695   ei = &element_info[element];
3696
3697   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3698     ei->description[i] = getFile8Bit(file);
3699   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3700
3701   group = element_info[element].group;
3702
3703   group->num_elements = getFile8Bit(file);
3704
3705   ei->use_gfx_element = getFile8Bit(file);
3706   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3707
3708   group->choice_mode = getFile8Bit(file);
3709
3710   // some free bytes for future values and padding
3711   ReadUnusedBytesFromFile(file, 3);
3712
3713   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3714     group->element[i] = getMappedElement(getFile16BitBE(file));
3715
3716   // mark this group element as modified
3717   element_info[element].modified_settings = TRUE;
3718
3719   level->file_has_custom_elements = TRUE;
3720
3721   return chunk_size;
3722 }
3723
3724 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3725                                 int element, int real_element)
3726 {
3727   int micro_chunk_size = 0;
3728   int conf_type = getFile8Bit(file);
3729   int byte_mask = conf_type & CONF_MASK_BYTES;
3730   boolean element_found = FALSE;
3731   int i;
3732
3733   micro_chunk_size += 1;
3734
3735   if (byte_mask == CONF_MASK_MULTI_BYTES)
3736   {
3737     int num_bytes = getFile16BitBE(file);
3738     byte *buffer = checked_malloc(num_bytes);
3739
3740     ReadBytesFromFile(file, buffer, num_bytes);
3741
3742     for (i = 0; conf[i].data_type != -1; i++)
3743     {
3744       if (conf[i].element == element &&
3745           conf[i].conf_type == conf_type)
3746       {
3747         int data_type = conf[i].data_type;
3748         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3749         int max_num_entities = conf[i].max_num_entities;
3750
3751         if (num_entities > max_num_entities)
3752         {
3753           Warn("truncating number of entities for element %d from %d to %d",
3754                element, num_entities, max_num_entities);
3755
3756           num_entities = max_num_entities;
3757         }
3758
3759         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3760                                   data_type == TYPE_CONTENT_LIST))
3761         {
3762           // for element and content lists, zero entities are not allowed
3763           Warn("found empty list of entities for element %d", element);
3764
3765           // do not set "num_entities" here to prevent reading behind buffer
3766
3767           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3768         }
3769         else
3770         {
3771           *(int *)(conf[i].num_entities) = num_entities;
3772         }
3773
3774         element_found = TRUE;
3775
3776         if (data_type == TYPE_STRING)
3777         {
3778           char *string = (char *)(conf[i].value);
3779           int j;
3780
3781           for (j = 0; j < max_num_entities; j++)
3782             string[j] = (j < num_entities ? buffer[j] : '\0');
3783         }
3784         else if (data_type == TYPE_ELEMENT_LIST)
3785         {
3786           int *element_array = (int *)(conf[i].value);
3787           int j;
3788
3789           for (j = 0; j < num_entities; j++)
3790             element_array[j] =
3791               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3792         }
3793         else if (data_type == TYPE_CONTENT_LIST)
3794         {
3795           struct Content *content= (struct Content *)(conf[i].value);
3796           int c, x, y;
3797
3798           for (c = 0; c < num_entities; c++)
3799             for (y = 0; y < 3; y++)
3800               for (x = 0; x < 3; x++)
3801                 content[c].e[x][y] =
3802                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3803         }
3804         else
3805           element_found = FALSE;
3806
3807         break;
3808       }
3809     }
3810
3811     checked_free(buffer);
3812
3813     micro_chunk_size += 2 + num_bytes;
3814   }
3815   else          // constant size configuration data (1, 2 or 4 bytes)
3816   {
3817     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3818                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3819                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3820
3821     for (i = 0; conf[i].data_type != -1; i++)
3822     {
3823       if (conf[i].element == element &&
3824           conf[i].conf_type == conf_type)
3825       {
3826         int data_type = conf[i].data_type;
3827
3828         if (data_type == TYPE_ELEMENT)
3829           value = getMappedElement(value);
3830
3831         if (data_type == TYPE_BOOLEAN)
3832           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3833         else
3834           *(int *)    (conf[i].value) = value;
3835
3836         element_found = TRUE;
3837
3838         break;
3839       }
3840     }
3841
3842     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3843   }
3844
3845   if (!element_found)
3846   {
3847     char *error_conf_chunk_bytes =
3848       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3849        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3850        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3851     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3852     int error_element = real_element;
3853
3854     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3855          error_conf_chunk_bytes, error_conf_chunk_token,
3856          error_element, EL_NAME(error_element));
3857   }
3858
3859   return micro_chunk_size;
3860 }
3861
3862 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3863 {
3864   int real_chunk_size = 0;
3865
3866   li = *level;          // copy level data into temporary buffer
3867
3868   while (!checkEndOfFile(file))
3869   {
3870     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3871
3872     if (real_chunk_size >= chunk_size)
3873       break;
3874   }
3875
3876   *level = li;          // copy temporary buffer back to level data
3877
3878   return real_chunk_size;
3879 }
3880
3881 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3882 {
3883   int real_chunk_size = 0;
3884
3885   li = *level;          // copy level data into temporary buffer
3886
3887   while (!checkEndOfFile(file))
3888   {
3889     int element = getMappedElement(getFile16BitBE(file));
3890
3891     real_chunk_size += 2;
3892     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3893                                             element, element);
3894     if (real_chunk_size >= chunk_size)
3895       break;
3896   }
3897
3898   *level = li;          // copy temporary buffer back to level data
3899
3900   return real_chunk_size;
3901 }
3902
3903 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3904 {
3905   int real_chunk_size = 0;
3906
3907   li = *level;          // copy level data into temporary buffer
3908
3909   while (!checkEndOfFile(file))
3910   {
3911     int element = getMappedElement(getFile16BitBE(file));
3912
3913     real_chunk_size += 2;
3914     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3915                                             element, element);
3916     if (real_chunk_size >= chunk_size)
3917       break;
3918   }
3919
3920   *level = li;          // copy temporary buffer back to level data
3921
3922   return real_chunk_size;
3923 }
3924
3925 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3926 {
3927   int element = getMappedElement(getFile16BitBE(file));
3928   int envelope_nr = element - EL_ENVELOPE_1;
3929   int real_chunk_size = 2;
3930
3931   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3932
3933   while (!checkEndOfFile(file))
3934   {
3935     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3936                                             -1, element);
3937
3938     if (real_chunk_size >= chunk_size)
3939       break;
3940   }
3941
3942   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3943
3944   return real_chunk_size;
3945 }
3946
3947 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3948 {
3949   int element = getMappedElement(getFile16BitBE(file));
3950   int real_chunk_size = 2;
3951   struct ElementInfo *ei = &element_info[element];
3952   int i;
3953
3954   xx_ei = *ei;          // copy element data into temporary buffer
3955
3956   xx_ei.num_change_pages = -1;
3957
3958   while (!checkEndOfFile(file))
3959   {
3960     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3961                                             -1, element);
3962     if (xx_ei.num_change_pages != -1)
3963       break;
3964
3965     if (real_chunk_size >= chunk_size)
3966       break;
3967   }
3968
3969   *ei = xx_ei;
3970
3971   if (ei->num_change_pages == -1)
3972   {
3973     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3974          EL_NAME(element));
3975
3976     ei->num_change_pages = 1;
3977
3978     setElementChangePages(ei, 1);
3979     setElementChangeInfoToDefaults(ei->change);
3980
3981     return real_chunk_size;
3982   }
3983
3984   // initialize number of change pages stored for this custom element
3985   setElementChangePages(ei, ei->num_change_pages);
3986   for (i = 0; i < ei->num_change_pages; i++)
3987     setElementChangeInfoToDefaults(&ei->change_page[i]);
3988
3989   // start with reading properties for the first change page
3990   xx_current_change_page = 0;
3991
3992   while (!checkEndOfFile(file))
3993   {
3994     // level file might contain invalid change page number
3995     if (xx_current_change_page >= ei->num_change_pages)
3996       break;
3997
3998     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3999
4000     xx_change = *change;        // copy change data into temporary buffer
4001
4002     resetEventBits();           // reset bits; change page might have changed
4003
4004     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4005                                             -1, element);
4006
4007     *change = xx_change;
4008
4009     setEventFlagsFromEventBits(change);
4010
4011     if (real_chunk_size >= chunk_size)
4012       break;
4013   }
4014
4015   level->file_has_custom_elements = TRUE;
4016
4017   return real_chunk_size;
4018 }
4019
4020 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4021 {
4022   int element = getMappedElement(getFile16BitBE(file));
4023   int real_chunk_size = 2;
4024   struct ElementInfo *ei = &element_info[element];
4025   struct ElementGroupInfo *group = ei->group;
4026
4027   if (group == NULL)
4028     return -1;
4029
4030   xx_ei = *ei;          // copy element data into temporary buffer
4031   xx_group = *group;    // copy group data into temporary buffer
4032
4033   while (!checkEndOfFile(file))
4034   {
4035     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4036                                             -1, element);
4037
4038     if (real_chunk_size >= chunk_size)
4039       break;
4040   }
4041
4042   *ei = xx_ei;
4043   *group = xx_group;
4044
4045   level->file_has_custom_elements = TRUE;
4046
4047   return real_chunk_size;
4048 }
4049
4050 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4051 {
4052   int element = getMappedElement(getFile16BitBE(file));
4053   int real_chunk_size = 2;
4054   struct ElementInfo *ei = &element_info[element];
4055
4056   xx_ei = *ei;          // copy element data into temporary buffer
4057
4058   while (!checkEndOfFile(file))
4059   {
4060     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4061                                             -1, element);
4062
4063     if (real_chunk_size >= chunk_size)
4064       break;
4065   }
4066
4067   *ei = xx_ei;
4068
4069   level->file_has_custom_elements = TRUE;
4070
4071   return real_chunk_size;
4072 }
4073
4074 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4075                                       struct LevelFileInfo *level_file_info,
4076                                       boolean level_info_only)
4077 {
4078   char *filename = level_file_info->filename;
4079   char cookie[MAX_LINE_LEN];
4080   char chunk_name[CHUNK_ID_LEN + 1];
4081   int chunk_size;
4082   File *file;
4083
4084   if (!(file = openFile(filename, MODE_READ)))
4085   {
4086     level->no_valid_file = TRUE;
4087     level->no_level_file = TRUE;
4088
4089     if (level_info_only)
4090       return;
4091
4092     Warn("cannot read level '%s' -- using empty level", filename);
4093
4094     if (!setup.editor.use_template_for_new_levels)
4095       return;
4096
4097     // if level file not found, try to initialize level data from template
4098     filename = getGlobalLevelTemplateFilename();
4099
4100     if (!(file = openFile(filename, MODE_READ)))
4101       return;
4102
4103     // default: for empty levels, use level template for custom elements
4104     level->use_custom_template = TRUE;
4105
4106     level->no_valid_file = FALSE;
4107   }
4108
4109   getFileChunkBE(file, chunk_name, NULL);
4110   if (strEqual(chunk_name, "RND1"))
4111   {
4112     getFile32BitBE(file);               // not used
4113
4114     getFileChunkBE(file, chunk_name, NULL);
4115     if (!strEqual(chunk_name, "CAVE"))
4116     {
4117       level->no_valid_file = TRUE;
4118
4119       Warn("unknown format of level file '%s'", filename);
4120
4121       closeFile(file);
4122
4123       return;
4124     }
4125   }
4126   else  // check for pre-2.0 file format with cookie string
4127   {
4128     strcpy(cookie, chunk_name);
4129     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4130       cookie[4] = '\0';
4131     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4132       cookie[strlen(cookie) - 1] = '\0';
4133
4134     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4135     {
4136       level->no_valid_file = TRUE;
4137
4138       Warn("unknown format of level file '%s'", filename);
4139
4140       closeFile(file);
4141
4142       return;
4143     }
4144
4145     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4146     {
4147       level->no_valid_file = TRUE;
4148
4149       Warn("unsupported version of level file '%s'", filename);
4150
4151       closeFile(file);
4152
4153       return;
4154     }
4155
4156     // pre-2.0 level files have no game version, so use file version here
4157     level->game_version = level->file_version;
4158   }
4159
4160   if (level->file_version < FILE_VERSION_1_2)
4161   {
4162     // level files from versions before 1.2.0 without chunk structure
4163     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4164     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4165   }
4166   else
4167   {
4168     static struct
4169     {
4170       char *name;
4171       int size;
4172       int (*loader)(File *, int, struct LevelInfo *);
4173     }
4174     chunk_info[] =
4175     {
4176       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4177       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4178       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4179       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4180       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4181       { "INFO", -1,                     LoadLevel_INFO },
4182       { "BODY", -1,                     LoadLevel_BODY },
4183       { "CONT", -1,                     LoadLevel_CONT },
4184       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4185       { "CNT3", -1,                     LoadLevel_CNT3 },
4186       { "CUS1", -1,                     LoadLevel_CUS1 },
4187       { "CUS2", -1,                     LoadLevel_CUS2 },
4188       { "CUS3", -1,                     LoadLevel_CUS3 },
4189       { "CUS4", -1,                     LoadLevel_CUS4 },
4190       { "GRP1", -1,                     LoadLevel_GRP1 },
4191       { "CONF", -1,                     LoadLevel_CONF },
4192       { "ELEM", -1,                     LoadLevel_ELEM },
4193       { "NOTE", -1,                     LoadLevel_NOTE },
4194       { "CUSX", -1,                     LoadLevel_CUSX },
4195       { "GRPX", -1,                     LoadLevel_GRPX },
4196       { "EMPX", -1,                     LoadLevel_EMPX },
4197
4198       {  NULL,  0,                      NULL }
4199     };
4200
4201     while (getFileChunkBE(file, chunk_name, &chunk_size))
4202     {
4203       int i = 0;
4204
4205       while (chunk_info[i].name != NULL &&
4206              !strEqual(chunk_name, chunk_info[i].name))
4207         i++;
4208
4209       if (chunk_info[i].name == NULL)
4210       {
4211         Warn("unknown chunk '%s' in level file '%s'",
4212              chunk_name, filename);
4213
4214         ReadUnusedBytesFromFile(file, chunk_size);
4215       }
4216       else if (chunk_info[i].size != -1 &&
4217                chunk_info[i].size != chunk_size)
4218       {
4219         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4220              chunk_size, chunk_name, filename);
4221
4222         ReadUnusedBytesFromFile(file, chunk_size);
4223       }
4224       else
4225       {
4226         // call function to load this level chunk
4227         int chunk_size_expected =
4228           (chunk_info[i].loader)(file, chunk_size, level);
4229
4230         if (chunk_size_expected < 0)
4231         {
4232           Warn("error reading chunk '%s' in level file '%s'",
4233                chunk_name, filename);
4234
4235           break;
4236         }
4237
4238         // the size of some chunks cannot be checked before reading other
4239         // chunks first (like "HEAD" and "BODY") that contain some header
4240         // information, so check them here
4241         if (chunk_size_expected != chunk_size)
4242         {
4243           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4244                chunk_size, chunk_name, filename);
4245
4246           break;
4247         }
4248       }
4249     }
4250   }
4251
4252   closeFile(file);
4253 }
4254
4255
4256 // ----------------------------------------------------------------------------
4257 // functions for loading BD level
4258 // ----------------------------------------------------------------------------
4259
4260 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4261 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4262
4263 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4264 {
4265   struct LevelInfo_BD *level_bd = level->native_bd_level;
4266   GdCave *cave = NULL;  // will be changed below
4267   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4268   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4269   int x, y;
4270
4271   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4272
4273   // cave and map newly allocated when set to defaults above
4274   cave = level_bd->cave;
4275
4276   // level type
4277   cave->intermission                    = level->bd_intermission;
4278
4279   // level settings
4280   cave->level_time[0]                   = level->time;
4281   cave->level_diamonds[0]               = level->gems_needed;
4282
4283   // game timing
4284   cave->scheduling                      = level->bd_scheduling_type;
4285   cave->pal_timing                      = level->bd_pal_timing;
4286   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4287   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4288   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4289   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4290
4291   // scores
4292   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4293   cave->diamond_value                   = level->score[SC_EMERALD];
4294   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4295
4296   // compatibility settings
4297   cave->lineshift                       = level->bd_line_shifting_borders;
4298   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4299   cave->short_explosions                = level->bd_short_explosions;
4300
4301   // player properties
4302   cave->diagonal_movements              = level->bd_diagonal_movements;
4303   cave->active_is_first_found           = level->bd_topmost_player_active;
4304   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4305   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4306   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4307   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4308
4309   // element properties
4310   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4311   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4312   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4313   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4314   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4315   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4316   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4317   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4318   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4319   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4320   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4321
4322   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4323   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4324   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4325   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4326   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4327   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4328   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4329
4330   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4331   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4332   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4333   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4334   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4335   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4336   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4337   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4338   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4339   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4340   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4341
4342   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4343   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4344   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4345   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4346   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4347   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4348
4349   cave->slime_predictable               = level->bd_slime_is_predictable;
4350   cave->slime_correct_random            = level->bd_slime_correct_random;
4351   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4352   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4353   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4354   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4355   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4356   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4357   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4358   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4359   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4360   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4361
4362   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4363   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4364   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4365
4366   cave->biter_delay_frame               = level->bd_biter_move_delay;
4367   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4368
4369   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4370
4371   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4372
4373   cave->replicators_active              = level->bd_replicators_active;
4374   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4375
4376   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4377   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4378
4379   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4380
4381   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4382
4383   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4384   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4385   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4386
4387   cave->infinite_rockets                = level->bd_infinite_rockets;
4388
4389   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4390   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4391
4392   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4393   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4394
4395   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4396   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4397   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4398
4399   cave->gravity                         = level->bd_gravity_direction;
4400   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4401   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4402   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4403
4404   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4405   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4406   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4407   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4408
4409   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4410   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4411   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4412   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4413   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4414   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4415
4416   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4417   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4418   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4419   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4420
4421   cave->colorb                          = level->bd_color_b;
4422   cave->color0                          = level->bd_color_0;
4423   cave->color1                          = level->bd_color_1;
4424   cave->color2                          = level->bd_color_2;
4425   cave->color3                          = level->bd_color_3;
4426   cave->color4                          = level->bd_color_4;
4427   cave->color5                          = level->bd_color_5;
4428
4429   // level name
4430   strncpy(cave->name, level->name, sizeof(GdString));
4431   cave->name[sizeof(GdString) - 1] = '\0';
4432
4433   // playfield elements
4434   for (x = 0; x < cave->w; x++)
4435     for (y = 0; y < cave->h; y++)
4436       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4437 }
4438
4439 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4440 {
4441   struct LevelInfo_BD *level_bd = level->native_bd_level;
4442   GdCave *cave = level_bd->cave;
4443   int bd_level_nr = level_bd->level_nr;
4444   int x, y;
4445
4446   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4447   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4448
4449   // level type
4450   level->bd_intermission                = cave->intermission;
4451
4452   // level settings
4453   level->time                           = cave->level_time[bd_level_nr];
4454   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4455
4456   // game timing
4457   level->bd_scheduling_type             = cave->scheduling;
4458   level->bd_pal_timing                  = cave->pal_timing;
4459   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4460   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4461   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4462   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4463
4464   // scores
4465   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4466   level->score[SC_EMERALD]              = cave->diamond_value;
4467   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4468
4469   // compatibility settings
4470   level->bd_line_shifting_borders       = cave->lineshift;
4471   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4472   level->bd_short_explosions            = cave->short_explosions;
4473
4474   // player properties
4475   level->bd_diagonal_movements          = cave->diagonal_movements;
4476   level->bd_topmost_player_active       = cave->active_is_first_found;
4477   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4478   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4479   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4480   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4481
4482   // element properties
4483   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4484   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4485   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4486   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4487   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4488   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4489   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4490   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4491   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4492   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4493   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4494
4495   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4496   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4497   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4498   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4499   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4500   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4501   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4502
4503   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4504   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4505   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4506   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4507   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4508   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4509   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4510   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4511   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4512   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4513   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4514
4515   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4516   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4517   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4518   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4519   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4520   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4521
4522   level->bd_slime_is_predictable        = cave->slime_predictable;
4523   level->bd_slime_correct_random        = cave->slime_correct_random;
4524   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4525   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4526   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4527   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4528   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4529   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4530   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4531   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4532   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4533   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4534
4535   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4536   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4537   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4538
4539   level->bd_biter_move_delay            = cave->biter_delay_frame;
4540   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4541
4542   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4543
4544   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4545
4546   level->bd_replicators_active          = cave->replicators_active;
4547   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4548
4549   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4550   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4551
4552   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4553
4554   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4555
4556   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4557   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4558   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4559
4560   level->bd_infinite_rockets            = cave->infinite_rockets;
4561
4562   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4563   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4564
4565   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4566   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4567
4568   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4569   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4570   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4571
4572   level->bd_gravity_direction           = cave->gravity;
4573   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4574   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4575   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4576
4577   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4578   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4579   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4580   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4581
4582   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4583   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4584   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4585   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4586   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4587   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4588
4589   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4590   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4591   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4592   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4593
4594   level->bd_color_b                     = cave->colorb;
4595   level->bd_color_0                     = cave->color0;
4596   level->bd_color_1                     = cave->color1;
4597   level->bd_color_2                     = cave->color2;
4598   level->bd_color_3                     = cave->color3;
4599   level->bd_color_4                     = cave->color4;
4600   level->bd_color_5                     = cave->color5;
4601
4602   // set default color type and colors for BD style level colors
4603   SetDefaultLevelColorType_BD();
4604   SetDefaultLevelColors_BD();
4605
4606   // level name
4607   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4608
4609   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4610   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4611
4612   // playfield elements
4613   for (x = 0; x < level->fieldx; x++)
4614     for (y = 0; y < level->fieldy; y++)
4615       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4616
4617   checked_free(cave_name);
4618 }
4619
4620 static void setTapeInfoToDefaults(void);
4621
4622 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4623 {
4624   struct LevelInfo_BD *level_bd = level->native_bd_level;
4625   GdCave *cave = level_bd->cave;
4626   GdReplay *replay = level_bd->replay;
4627   int i;
4628
4629   if (replay == NULL)
4630     return;
4631
4632   // always start with reliable default values
4633   setTapeInfoToDefaults();
4634
4635   tape.level_nr = level_nr;             // (currently not used)
4636   tape.random_seed = replay->seed;
4637
4638   TapeSetDateFromIsoDateString(replay->date);
4639
4640   tape.counter = 0;
4641   tape.pos[tape.counter].delay = 0;
4642
4643   tape.bd_replay = TRUE;
4644
4645   // all time calculations only used to display approximate tape time
4646   int cave_speed = cave->speed;
4647   int milliseconds_game = 0;
4648   int milliseconds_elapsed = 20;
4649
4650   for (i = 0; i < replay->movements->len; i++)
4651   {
4652     int replay_action = replay->movements->data[i];
4653     int tape_action = map_action_BD_to_RND(replay_action);
4654     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4655     boolean success = 0;
4656
4657     while (1)
4658     {
4659       success = TapeAddAction(action);
4660
4661       milliseconds_game += milliseconds_elapsed;
4662
4663       if (milliseconds_game >= cave_speed)
4664       {
4665         milliseconds_game -= cave_speed;
4666
4667         break;
4668       }
4669     }
4670
4671     tape.counter++;
4672     tape.pos[tape.counter].delay = 0;
4673     tape.pos[tape.counter].action[0] = 0;
4674
4675     if (!success)
4676     {
4677       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4678
4679       break;
4680     }
4681   }
4682
4683   TapeHaltRecording();
4684
4685   if (!replay->success)
4686     Warn("BD replay is marked as not successful");
4687 }
4688
4689
4690 // ----------------------------------------------------------------------------
4691 // functions for loading EM level
4692 // ----------------------------------------------------------------------------
4693
4694 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4695 {
4696   static int ball_xy[8][2] =
4697   {
4698     { 0, 0 },
4699     { 1, 0 },
4700     { 2, 0 },
4701     { 0, 1 },
4702     { 2, 1 },
4703     { 0, 2 },
4704     { 1, 2 },
4705     { 2, 2 },
4706   };
4707   struct LevelInfo_EM *level_em = level->native_em_level;
4708   struct CAVE *cav = level_em->cav;
4709   int i, j, x, y;
4710
4711   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4712   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4713
4714   cav->time_seconds     = level->time;
4715   cav->gems_needed      = level->gems_needed;
4716
4717   cav->emerald_score    = level->score[SC_EMERALD];
4718   cav->diamond_score    = level->score[SC_DIAMOND];
4719   cav->alien_score      = level->score[SC_ROBOT];
4720   cav->tank_score       = level->score[SC_SPACESHIP];
4721   cav->bug_score        = level->score[SC_BUG];
4722   cav->eater_score      = level->score[SC_YAMYAM];
4723   cav->nut_score        = level->score[SC_NUT];
4724   cav->dynamite_score   = level->score[SC_DYNAMITE];
4725   cav->key_score        = level->score[SC_KEY];
4726   cav->exit_score       = level->score[SC_TIME_BONUS];
4727
4728   cav->num_eater_arrays = level->num_yamyam_contents;
4729
4730   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4731     for (y = 0; y < 3; y++)
4732       for (x = 0; x < 3; x++)
4733         cav->eater_array[i][y * 3 + x] =
4734           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4735
4736   cav->amoeba_time              = level->amoeba_speed;
4737   cav->wonderwall_time          = level->time_magic_wall;
4738   cav->wheel_time               = level->time_wheel;
4739
4740   cav->android_move_time        = level->android_move_time;
4741   cav->android_clone_time       = level->android_clone_time;
4742   cav->ball_random              = level->ball_random;
4743   cav->ball_active              = level->ball_active_initial;
4744   cav->ball_time                = level->ball_time;
4745   cav->num_ball_arrays          = level->num_ball_contents;
4746
4747   cav->lenses_score             = level->lenses_score;
4748   cav->magnify_score            = level->magnify_score;
4749   cav->slurp_score              = level->slurp_score;
4750
4751   cav->lenses_time              = level->lenses_time;
4752   cav->magnify_time             = level->magnify_time;
4753
4754   cav->wind_time = 9999;
4755   cav->wind_direction =
4756     map_direction_RND_to_EM(level->wind_direction_initial);
4757
4758   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4759     for (j = 0; j < 8; j++)
4760       cav->ball_array[i][j] =
4761         map_element_RND_to_EM_cave(level->ball_content[i].
4762                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4763
4764   map_android_clone_elements_RND_to_EM(level);
4765
4766   // first fill the complete playfield with the empty space element
4767   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4768     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4769       cav->cave[x][y] = Cblank;
4770
4771   // then copy the real level contents from level file into the playfield
4772   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4773   {
4774     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4775
4776     if (level->field[x][y] == EL_AMOEBA_DEAD)
4777       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4778
4779     cav->cave[x][y] = new_element;
4780   }
4781
4782   for (i = 0; i < MAX_PLAYERS; i++)
4783   {
4784     cav->player_x[i] = -1;
4785     cav->player_y[i] = -1;
4786   }
4787
4788   // initialize player positions and delete players from the playfield
4789   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4790   {
4791     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4792     {
4793       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4794
4795       cav->player_x[player_nr] = x;
4796       cav->player_y[player_nr] = y;
4797
4798       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4799     }
4800   }
4801 }
4802
4803 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4804 {
4805   static int ball_xy[8][2] =
4806   {
4807     { 0, 0 },
4808     { 1, 0 },
4809     { 2, 0 },
4810     { 0, 1 },
4811     { 2, 1 },
4812     { 0, 2 },
4813     { 1, 2 },
4814     { 2, 2 },
4815   };
4816   struct LevelInfo_EM *level_em = level->native_em_level;
4817   struct CAVE *cav = level_em->cav;
4818   int i, j, x, y;
4819
4820   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4821   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4822
4823   level->time        = cav->time_seconds;
4824   level->gems_needed = cav->gems_needed;
4825
4826   sprintf(level->name, "Level %d", level->file_info.nr);
4827
4828   level->score[SC_EMERALD]      = cav->emerald_score;
4829   level->score[SC_DIAMOND]      = cav->diamond_score;
4830   level->score[SC_ROBOT]        = cav->alien_score;
4831   level->score[SC_SPACESHIP]    = cav->tank_score;
4832   level->score[SC_BUG]          = cav->bug_score;
4833   level->score[SC_YAMYAM]       = cav->eater_score;
4834   level->score[SC_NUT]          = cav->nut_score;
4835   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4836   level->score[SC_KEY]          = cav->key_score;
4837   level->score[SC_TIME_BONUS]   = cav->exit_score;
4838
4839   level->num_yamyam_contents    = cav->num_eater_arrays;
4840
4841   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4842     for (y = 0; y < 3; y++)
4843       for (x = 0; x < 3; x++)
4844         level->yamyam_content[i].e[x][y] =
4845           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4846
4847   level->amoeba_speed           = cav->amoeba_time;
4848   level->time_magic_wall        = cav->wonderwall_time;
4849   level->time_wheel             = cav->wheel_time;
4850
4851   level->android_move_time      = cav->android_move_time;
4852   level->android_clone_time     = cav->android_clone_time;
4853   level->ball_random            = cav->ball_random;
4854   level->ball_active_initial    = cav->ball_active;
4855   level->ball_time              = cav->ball_time;
4856   level->num_ball_contents      = cav->num_ball_arrays;
4857
4858   level->lenses_score           = cav->lenses_score;
4859   level->magnify_score          = cav->magnify_score;
4860   level->slurp_score            = cav->slurp_score;
4861
4862   level->lenses_time            = cav->lenses_time;
4863   level->magnify_time           = cav->magnify_time;
4864
4865   level->wind_direction_initial =
4866     map_direction_EM_to_RND(cav->wind_direction);
4867
4868   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4869     for (j = 0; j < 8; j++)
4870       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4871         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4872
4873   map_android_clone_elements_EM_to_RND(level);
4874
4875   // convert the playfield (some elements need special treatment)
4876   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4877   {
4878     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4879
4880     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4881       new_element = EL_AMOEBA_DEAD;
4882
4883     level->field[x][y] = new_element;
4884   }
4885
4886   for (i = 0; i < MAX_PLAYERS; i++)
4887   {
4888     // in case of all players set to the same field, use the first player
4889     int nr = MAX_PLAYERS - i - 1;
4890     int jx = cav->player_x[nr];
4891     int jy = cav->player_y[nr];
4892
4893     if (jx != -1 && jy != -1)
4894       level->field[jx][jy] = EL_PLAYER_1 + nr;
4895   }
4896
4897   // time score is counted for each 10 seconds left in Emerald Mine levels
4898   level->time_score_base = 10;
4899 }
4900
4901
4902 // ----------------------------------------------------------------------------
4903 // functions for loading SP level
4904 // ----------------------------------------------------------------------------
4905
4906 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4907 {
4908   struct LevelInfo_SP *level_sp = level->native_sp_level;
4909   LevelInfoType *header = &level_sp->header;
4910   int i, x, y;
4911
4912   level_sp->width  = level->fieldx;
4913   level_sp->height = level->fieldy;
4914
4915   for (x = 0; x < level->fieldx; x++)
4916     for (y = 0; y < level->fieldy; y++)
4917       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4918
4919   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4920
4921   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4922     header->LevelTitle[i] = level->name[i];
4923   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4924
4925   header->InfotronsNeeded = level->gems_needed;
4926
4927   header->SpecialPortCount = 0;
4928
4929   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4930   {
4931     boolean gravity_port_found = FALSE;
4932     boolean gravity_port_valid = FALSE;
4933     int gravity_port_flag;
4934     int gravity_port_base_element;
4935     int element = level->field[x][y];
4936
4937     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4938         element <= EL_SP_GRAVITY_ON_PORT_UP)
4939     {
4940       gravity_port_found = TRUE;
4941       gravity_port_valid = TRUE;
4942       gravity_port_flag = 1;
4943       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4944     }
4945     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4946              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4947     {
4948       gravity_port_found = TRUE;
4949       gravity_port_valid = TRUE;
4950       gravity_port_flag = 0;
4951       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4952     }
4953     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4954              element <= EL_SP_GRAVITY_PORT_UP)
4955     {
4956       // change R'n'D style gravity inverting special port to normal port
4957       // (there are no gravity inverting ports in native Supaplex engine)
4958
4959       gravity_port_found = TRUE;
4960       gravity_port_valid = FALSE;
4961       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4962     }
4963
4964     if (gravity_port_found)
4965     {
4966       if (gravity_port_valid &&
4967           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4968       {
4969         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4970
4971         port->PortLocation = (y * level->fieldx + x) * 2;
4972         port->Gravity = gravity_port_flag;
4973
4974         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4975
4976         header->SpecialPortCount++;
4977       }
4978       else
4979       {
4980         // change special gravity port to normal port
4981
4982         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4983       }
4984
4985       level_sp->playfield[x][y] = element - EL_SP_START;
4986     }
4987   }
4988 }
4989
4990 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4991 {
4992   struct LevelInfo_SP *level_sp = level->native_sp_level;
4993   LevelInfoType *header = &level_sp->header;
4994   boolean num_invalid_elements = 0;
4995   int i, j, x, y;
4996
4997   level->fieldx = level_sp->width;
4998   level->fieldy = level_sp->height;
4999
5000   for (x = 0; x < level->fieldx; x++)
5001   {
5002     for (y = 0; y < level->fieldy; y++)
5003     {
5004       int element_old = level_sp->playfield[x][y];
5005       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5006
5007       if (element_new == EL_UNKNOWN)
5008       {
5009         num_invalid_elements++;
5010
5011         Debug("level:native:SP", "invalid element %d at position %d, %d",
5012               element_old, x, y);
5013       }
5014
5015       level->field[x][y] = element_new;
5016     }
5017   }
5018
5019   if (num_invalid_elements > 0)
5020     Warn("found %d invalid elements%s", num_invalid_elements,
5021          (!options.debug ? " (use '--debug' for more details)" : ""));
5022
5023   for (i = 0; i < MAX_PLAYERS; i++)
5024     level->initial_player_gravity[i] =
5025       (header->InitialGravity == 1 ? TRUE : FALSE);
5026
5027   // skip leading spaces
5028   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5029     if (header->LevelTitle[i] != ' ')
5030       break;
5031
5032   // copy level title
5033   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5034     level->name[j] = header->LevelTitle[i];
5035   level->name[j] = '\0';
5036
5037   // cut trailing spaces
5038   for (; j > 0; j--)
5039     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5040       level->name[j - 1] = '\0';
5041
5042   level->gems_needed = header->InfotronsNeeded;
5043
5044   for (i = 0; i < header->SpecialPortCount; i++)
5045   {
5046     SpecialPortType *port = &header->SpecialPort[i];
5047     int port_location = port->PortLocation;
5048     int gravity = port->Gravity;
5049     int port_x, port_y, port_element;
5050
5051     port_x = (port_location / 2) % level->fieldx;
5052     port_y = (port_location / 2) / level->fieldx;
5053
5054     if (port_x < 0 || port_x >= level->fieldx ||
5055         port_y < 0 || port_y >= level->fieldy)
5056     {
5057       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5058
5059       continue;
5060     }
5061
5062     port_element = level->field[port_x][port_y];
5063
5064     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5065         port_element > EL_SP_GRAVITY_PORT_UP)
5066     {
5067       Warn("no special port at position (%d, %d)", port_x, port_y);
5068
5069       continue;
5070     }
5071
5072     // change previous (wrong) gravity inverting special port to either
5073     // gravity enabling special port or gravity disabling special port
5074     level->field[port_x][port_y] +=
5075       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5076        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5077   }
5078
5079   // change special gravity ports without database entries to normal ports
5080   for (x = 0; x < level->fieldx; x++)
5081     for (y = 0; y < level->fieldy; y++)
5082       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5083           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5084         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5085
5086   level->time = 0;                      // no time limit
5087   level->amoeba_speed = 0;
5088   level->time_magic_wall = 0;
5089   level->time_wheel = 0;
5090   level->amoeba_content = EL_EMPTY;
5091
5092   // original Supaplex does not use score values -- rate by playing time
5093   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5094     level->score[i] = 0;
5095
5096   level->rate_time_over_score = TRUE;
5097
5098   // there are no yamyams in supaplex levels
5099   for (i = 0; i < level->num_yamyam_contents; i++)
5100     for (x = 0; x < 3; x++)
5101       for (y = 0; y < 3; y++)
5102         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5103 }
5104
5105 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5106 {
5107   struct LevelInfo_SP *level_sp = level->native_sp_level;
5108   struct DemoInfo_SP *demo = &level_sp->demo;
5109   int i, j;
5110
5111   // always start with reliable default values
5112   demo->is_available = FALSE;
5113   demo->length = 0;
5114
5115   if (TAPE_IS_EMPTY(tape))
5116     return;
5117
5118   demo->level_nr = tape.level_nr;       // (currently not used)
5119
5120   level_sp->header.DemoRandomSeed = tape.random_seed;
5121
5122   demo->length = 0;
5123
5124   for (i = 0; i < tape.length; i++)
5125   {
5126     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5127     int demo_repeat = tape.pos[i].delay;
5128     int demo_entries = (demo_repeat + 15) / 16;
5129
5130     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5131     {
5132       Warn("tape truncated: size exceeds maximum SP demo size %d",
5133            SP_MAX_TAPE_LEN);
5134
5135       break;
5136     }
5137
5138     for (j = 0; j < demo_repeat / 16; j++)
5139       demo->data[demo->length++] = 0xf0 | demo_action;
5140
5141     if (demo_repeat % 16)
5142       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5143   }
5144
5145   demo->is_available = TRUE;
5146 }
5147
5148 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5149 {
5150   struct LevelInfo_SP *level_sp = level->native_sp_level;
5151   struct DemoInfo_SP *demo = &level_sp->demo;
5152   char *filename = level->file_info.filename;
5153   int i;
5154
5155   // always start with reliable default values
5156   setTapeInfoToDefaults();
5157
5158   if (!demo->is_available)
5159     return;
5160
5161   tape.level_nr = demo->level_nr;       // (currently not used)
5162   tape.random_seed = level_sp->header.DemoRandomSeed;
5163
5164   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5165
5166   tape.counter = 0;
5167   tape.pos[tape.counter].delay = 0;
5168
5169   for (i = 0; i < demo->length; i++)
5170   {
5171     int demo_action = demo->data[i] & 0x0f;
5172     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5173     int tape_action = map_key_SP_to_RND(demo_action);
5174     int tape_repeat = demo_repeat + 1;
5175     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5176     boolean success = 0;
5177     int j;
5178
5179     for (j = 0; j < tape_repeat; j++)
5180       success = TapeAddAction(action);
5181
5182     if (!success)
5183     {
5184       Warn("SP demo truncated: size exceeds maximum tape size %d",
5185            MAX_TAPE_LEN);
5186
5187       break;
5188     }
5189   }
5190
5191   TapeHaltRecording();
5192 }
5193
5194
5195 // ----------------------------------------------------------------------------
5196 // functions for loading MM level
5197 // ----------------------------------------------------------------------------
5198
5199 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5200 {
5201   struct LevelInfo_MM *level_mm = level->native_mm_level;
5202   int i, x, y;
5203
5204   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5205   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5206
5207   level_mm->time = level->time;
5208   level_mm->kettles_needed = level->gems_needed;
5209   level_mm->auto_count_kettles = level->auto_count_gems;
5210
5211   level_mm->mm_laser_red   = level->mm_laser_red;
5212   level_mm->mm_laser_green = level->mm_laser_green;
5213   level_mm->mm_laser_blue  = level->mm_laser_blue;
5214
5215   level_mm->df_laser_red   = level->df_laser_red;
5216   level_mm->df_laser_green = level->df_laser_green;
5217   level_mm->df_laser_blue  = level->df_laser_blue;
5218
5219   strcpy(level_mm->name, level->name);
5220   strcpy(level_mm->author, level->author);
5221
5222   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5223   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5224   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5225   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5226   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5227
5228   level_mm->amoeba_speed = level->amoeba_speed;
5229   level_mm->time_fuse    = level->mm_time_fuse;
5230   level_mm->time_bomb    = level->mm_time_bomb;
5231   level_mm->time_ball    = level->mm_time_ball;
5232   level_mm->time_block   = level->mm_time_block;
5233
5234   level_mm->num_ball_contents = level->num_mm_ball_contents;
5235   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5236   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5237   level_mm->explode_ball = level->explode_mm_ball;
5238
5239   for (i = 0; i < level->num_mm_ball_contents; i++)
5240     level_mm->ball_content[i] =
5241       map_element_RND_to_MM(level->mm_ball_content[i]);
5242
5243   for (x = 0; x < level->fieldx; x++)
5244     for (y = 0; y < level->fieldy; y++)
5245       Ur[x][y] =
5246         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5247 }
5248
5249 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5250 {
5251   struct LevelInfo_MM *level_mm = level->native_mm_level;
5252   int i, x, y;
5253
5254   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5255   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5256
5257   level->time = level_mm->time;
5258   level->gems_needed = level_mm->kettles_needed;
5259   level->auto_count_gems = level_mm->auto_count_kettles;
5260
5261   level->mm_laser_red   = level_mm->mm_laser_red;
5262   level->mm_laser_green = level_mm->mm_laser_green;
5263   level->mm_laser_blue  = level_mm->mm_laser_blue;
5264
5265   level->df_laser_red   = level_mm->df_laser_red;
5266   level->df_laser_green = level_mm->df_laser_green;
5267   level->df_laser_blue  = level_mm->df_laser_blue;
5268
5269   strcpy(level->name, level_mm->name);
5270
5271   // only overwrite author from 'levelinfo.conf' if author defined in level
5272   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5273     strcpy(level->author, level_mm->author);
5274
5275   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5276   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5277   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5278   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5279   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5280
5281   level->amoeba_speed  = level_mm->amoeba_speed;
5282   level->mm_time_fuse  = level_mm->time_fuse;
5283   level->mm_time_bomb  = level_mm->time_bomb;
5284   level->mm_time_ball  = level_mm->time_ball;
5285   level->mm_time_block = level_mm->time_block;
5286
5287   level->num_mm_ball_contents = level_mm->num_ball_contents;
5288   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5289   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5290   level->explode_mm_ball = level_mm->explode_ball;
5291
5292   for (i = 0; i < level->num_mm_ball_contents; i++)
5293     level->mm_ball_content[i] =
5294       map_element_MM_to_RND(level_mm->ball_content[i]);
5295
5296   for (x = 0; x < level->fieldx; x++)
5297     for (y = 0; y < level->fieldy; y++)
5298       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5299 }
5300
5301
5302 // ----------------------------------------------------------------------------
5303 // functions for loading DC level
5304 // ----------------------------------------------------------------------------
5305
5306 #define DC_LEVEL_HEADER_SIZE            344
5307
5308 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5309                                         boolean init)
5310 {
5311   static int last_data_encoded;
5312   static int offset1;
5313   static int offset2;
5314   int diff;
5315   int diff_hi, diff_lo;
5316   int data_hi, data_lo;
5317   unsigned short data_decoded;
5318
5319   if (init)
5320   {
5321     last_data_encoded = 0;
5322     offset1 = -1;
5323     offset2 = 0;
5324
5325     return 0;
5326   }
5327
5328   diff = data_encoded - last_data_encoded;
5329   diff_hi = diff & ~0xff;
5330   diff_lo = diff &  0xff;
5331
5332   offset2 += diff_lo;
5333
5334   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5335   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5336   data_hi = data_hi & 0xff00;
5337
5338   data_decoded = data_hi | data_lo;
5339
5340   last_data_encoded = data_encoded;
5341
5342   offset1 = (offset1 + 1) % 31;
5343   offset2 = offset2 & 0xff;
5344
5345   return data_decoded;
5346 }
5347
5348 static int getMappedElement_DC(int element)
5349 {
5350   switch (element)
5351   {
5352     case 0x0000:
5353       element = EL_ROCK;
5354       break;
5355
5356       // 0x0117 - 0x036e: (?)
5357       // EL_DIAMOND
5358
5359       // 0x042d - 0x0684: (?)
5360       // EL_EMERALD
5361
5362     case 0x06f1:
5363       element = EL_NUT;
5364       break;
5365
5366     case 0x074c:
5367       element = EL_BOMB;
5368       break;
5369
5370     case 0x07a4:
5371       element = EL_PEARL;
5372       break;
5373
5374     case 0x0823:
5375       element = EL_CRYSTAL;
5376       break;
5377
5378     case 0x0e77:        // quicksand (boulder)
5379       element = EL_QUICKSAND_FAST_FULL;
5380       break;
5381
5382     case 0x0e99:        // slow quicksand (boulder)
5383       element = EL_QUICKSAND_FULL;
5384       break;
5385
5386     case 0x0ed2:
5387       element = EL_EM_EXIT_OPEN;
5388       break;
5389
5390     case 0x0ee3:
5391       element = EL_EM_EXIT_CLOSED;
5392       break;
5393
5394     case 0x0eeb:
5395       element = EL_EM_STEEL_EXIT_OPEN;
5396       break;
5397
5398     case 0x0efc:
5399       element = EL_EM_STEEL_EXIT_CLOSED;
5400       break;
5401
5402     case 0x0f4f:        // dynamite (lit 1)
5403       element = EL_EM_DYNAMITE_ACTIVE;
5404       break;
5405
5406     case 0x0f57:        // dynamite (lit 2)
5407       element = EL_EM_DYNAMITE_ACTIVE;
5408       break;
5409
5410     case 0x0f5f:        // dynamite (lit 3)
5411       element = EL_EM_DYNAMITE_ACTIVE;
5412       break;
5413
5414     case 0x0f67:        // dynamite (lit 4)
5415       element = EL_EM_DYNAMITE_ACTIVE;
5416       break;
5417
5418     case 0x0f81:
5419     case 0x0f82:
5420     case 0x0f83:
5421     case 0x0f84:
5422       element = EL_AMOEBA_WET;
5423       break;
5424
5425     case 0x0f85:
5426       element = EL_AMOEBA_DROP;
5427       break;
5428
5429     case 0x0fb9:
5430       element = EL_DC_MAGIC_WALL;
5431       break;
5432
5433     case 0x0fd0:
5434       element = EL_SPACESHIP_UP;
5435       break;
5436
5437     case 0x0fd9:
5438       element = EL_SPACESHIP_DOWN;
5439       break;
5440
5441     case 0x0ff1:
5442       element = EL_SPACESHIP_LEFT;
5443       break;
5444
5445     case 0x0ff9:
5446       element = EL_SPACESHIP_RIGHT;
5447       break;
5448
5449     case 0x1057:
5450       element = EL_BUG_UP;
5451       break;
5452
5453     case 0x1060:
5454       element = EL_BUG_DOWN;
5455       break;
5456
5457     case 0x1078:
5458       element = EL_BUG_LEFT;
5459       break;
5460
5461     case 0x1080:
5462       element = EL_BUG_RIGHT;
5463       break;
5464
5465     case 0x10de:
5466       element = EL_MOLE_UP;
5467       break;
5468
5469     case 0x10e7:
5470       element = EL_MOLE_DOWN;
5471       break;
5472
5473     case 0x10ff:
5474       element = EL_MOLE_LEFT;
5475       break;
5476
5477     case 0x1107:
5478       element = EL_MOLE_RIGHT;
5479       break;
5480
5481     case 0x11c0:
5482       element = EL_ROBOT;
5483       break;
5484
5485     case 0x13f5:
5486       element = EL_YAMYAM_UP;
5487       break;
5488
5489     case 0x1425:
5490       element = EL_SWITCHGATE_OPEN;
5491       break;
5492
5493     case 0x1426:
5494       element = EL_SWITCHGATE_CLOSED;
5495       break;
5496
5497     case 0x1437:
5498       element = EL_DC_SWITCHGATE_SWITCH_UP;
5499       break;
5500
5501     case 0x143a:
5502       element = EL_TIMEGATE_CLOSED;
5503       break;
5504
5505     case 0x144c:        // conveyor belt switch (green)
5506       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5507       break;
5508
5509     case 0x144f:        // conveyor belt switch (red)
5510       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5511       break;
5512
5513     case 0x1452:        // conveyor belt switch (blue)
5514       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5515       break;
5516
5517     case 0x145b:
5518       element = EL_CONVEYOR_BELT_3_MIDDLE;
5519       break;
5520
5521     case 0x1463:
5522       element = EL_CONVEYOR_BELT_3_LEFT;
5523       break;
5524
5525     case 0x146b:
5526       element = EL_CONVEYOR_BELT_3_RIGHT;
5527       break;
5528
5529     case 0x1473:
5530       element = EL_CONVEYOR_BELT_1_MIDDLE;
5531       break;
5532
5533     case 0x147b:
5534       element = EL_CONVEYOR_BELT_1_LEFT;
5535       break;
5536
5537     case 0x1483:
5538       element = EL_CONVEYOR_BELT_1_RIGHT;
5539       break;
5540
5541     case 0x148b:
5542       element = EL_CONVEYOR_BELT_4_MIDDLE;
5543       break;
5544
5545     case 0x1493:
5546       element = EL_CONVEYOR_BELT_4_LEFT;
5547       break;
5548
5549     case 0x149b:
5550       element = EL_CONVEYOR_BELT_4_RIGHT;
5551       break;
5552
5553     case 0x14ac:
5554       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5555       break;
5556
5557     case 0x14bd:
5558       element = EL_EXPANDABLE_WALL_VERTICAL;
5559       break;
5560
5561     case 0x14c6:
5562       element = EL_EXPANDABLE_WALL_ANY;
5563       break;
5564
5565     case 0x14ce:        // growing steel wall (left/right)
5566       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5567       break;
5568
5569     case 0x14df:        // growing steel wall (up/down)
5570       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5571       break;
5572
5573     case 0x14e8:        // growing steel wall (up/down/left/right)
5574       element = EL_EXPANDABLE_STEELWALL_ANY;
5575       break;
5576
5577     case 0x14e9:
5578       element = EL_SHIELD_DEADLY;
5579       break;
5580
5581     case 0x1501:
5582       element = EL_EXTRA_TIME;
5583       break;
5584
5585     case 0x154f:
5586       element = EL_ACID;
5587       break;
5588
5589     case 0x1577:
5590       element = EL_EMPTY_SPACE;
5591       break;
5592
5593     case 0x1578:        // quicksand (empty)
5594       element = EL_QUICKSAND_FAST_EMPTY;
5595       break;
5596
5597     case 0x1579:        // slow quicksand (empty)
5598       element = EL_QUICKSAND_EMPTY;
5599       break;
5600
5601       // 0x157c - 0x158b:
5602       // EL_SAND
5603
5604       // 0x1590 - 0x159f:
5605       // EL_DC_LANDMINE
5606
5607     case 0x15a0:
5608       element = EL_EM_DYNAMITE;
5609       break;
5610
5611     case 0x15a1:        // key (red)
5612       element = EL_EM_KEY_1;
5613       break;
5614
5615     case 0x15a2:        // key (yellow)
5616       element = EL_EM_KEY_2;
5617       break;
5618
5619     case 0x15a3:        // key (blue)
5620       element = EL_EM_KEY_4;
5621       break;
5622
5623     case 0x15a4:        // key (green)
5624       element = EL_EM_KEY_3;
5625       break;
5626
5627     case 0x15a5:        // key (white)
5628       element = EL_DC_KEY_WHITE;
5629       break;
5630
5631     case 0x15a6:
5632       element = EL_WALL_SLIPPERY;
5633       break;
5634
5635     case 0x15a7:
5636       element = EL_WALL;
5637       break;
5638
5639     case 0x15a8:        // wall (not round)
5640       element = EL_WALL;
5641       break;
5642
5643     case 0x15a9:        // (blue)
5644       element = EL_CHAR_A;
5645       break;
5646
5647     case 0x15aa:        // (blue)
5648       element = EL_CHAR_B;
5649       break;
5650
5651     case 0x15ab:        // (blue)
5652       element = EL_CHAR_C;
5653       break;
5654
5655     case 0x15ac:        // (blue)
5656       element = EL_CHAR_D;
5657       break;
5658
5659     case 0x15ad:        // (blue)
5660       element = EL_CHAR_E;
5661       break;
5662
5663     case 0x15ae:        // (blue)
5664       element = EL_CHAR_F;
5665       break;
5666
5667     case 0x15af:        // (blue)
5668       element = EL_CHAR_G;
5669       break;
5670
5671     case 0x15b0:        // (blue)
5672       element = EL_CHAR_H;
5673       break;
5674
5675     case 0x15b1:        // (blue)
5676       element = EL_CHAR_I;
5677       break;
5678
5679     case 0x15b2:        // (blue)
5680       element = EL_CHAR_J;
5681       break;
5682
5683     case 0x15b3:        // (blue)
5684       element = EL_CHAR_K;
5685       break;
5686
5687     case 0x15b4:        // (blue)
5688       element = EL_CHAR_L;
5689       break;
5690
5691     case 0x15b5:        // (blue)
5692       element = EL_CHAR_M;
5693       break;
5694
5695     case 0x15b6:        // (blue)
5696       element = EL_CHAR_N;
5697       break;
5698
5699     case 0x15b7:        // (blue)
5700       element = EL_CHAR_O;
5701       break;
5702
5703     case 0x15b8:        // (blue)
5704       element = EL_CHAR_P;
5705       break;
5706
5707     case 0x15b9:        // (blue)
5708       element = EL_CHAR_Q;
5709       break;
5710
5711     case 0x15ba:        // (blue)
5712       element = EL_CHAR_R;
5713       break;
5714
5715     case 0x15bb:        // (blue)
5716       element = EL_CHAR_S;
5717       break;
5718
5719     case 0x15bc:        // (blue)
5720       element = EL_CHAR_T;
5721       break;
5722
5723     case 0x15bd:        // (blue)
5724       element = EL_CHAR_U;
5725       break;
5726
5727     case 0x15be:        // (blue)
5728       element = EL_CHAR_V;
5729       break;
5730
5731     case 0x15bf:        // (blue)
5732       element = EL_CHAR_W;
5733       break;
5734
5735     case 0x15c0:        // (blue)
5736       element = EL_CHAR_X;
5737       break;
5738
5739     case 0x15c1:        // (blue)
5740       element = EL_CHAR_Y;
5741       break;
5742
5743     case 0x15c2:        // (blue)
5744       element = EL_CHAR_Z;
5745       break;
5746
5747     case 0x15c3:        // (blue)
5748       element = EL_CHAR_AUMLAUT;
5749       break;
5750
5751     case 0x15c4:        // (blue)
5752       element = EL_CHAR_OUMLAUT;
5753       break;
5754
5755     case 0x15c5:        // (blue)
5756       element = EL_CHAR_UUMLAUT;
5757       break;
5758
5759     case 0x15c6:        // (blue)
5760       element = EL_CHAR_0;
5761       break;
5762
5763     case 0x15c7:        // (blue)
5764       element = EL_CHAR_1;
5765       break;
5766
5767     case 0x15c8:        // (blue)
5768       element = EL_CHAR_2;
5769       break;
5770
5771     case 0x15c9:        // (blue)
5772       element = EL_CHAR_3;
5773       break;
5774
5775     case 0x15ca:        // (blue)
5776       element = EL_CHAR_4;
5777       break;
5778
5779     case 0x15cb:        // (blue)
5780       element = EL_CHAR_5;
5781       break;
5782
5783     case 0x15cc:        // (blue)
5784       element = EL_CHAR_6;
5785       break;
5786
5787     case 0x15cd:        // (blue)
5788       element = EL_CHAR_7;
5789       break;
5790
5791     case 0x15ce:        // (blue)
5792       element = EL_CHAR_8;
5793       break;
5794
5795     case 0x15cf:        // (blue)
5796       element = EL_CHAR_9;
5797       break;
5798
5799     case 0x15d0:        // (blue)
5800       element = EL_CHAR_PERIOD;
5801       break;
5802
5803     case 0x15d1:        // (blue)
5804       element = EL_CHAR_EXCLAM;
5805       break;
5806
5807     case 0x15d2:        // (blue)
5808       element = EL_CHAR_COLON;
5809       break;
5810
5811     case 0x15d3:        // (blue)
5812       element = EL_CHAR_LESS;
5813       break;
5814
5815     case 0x15d4:        // (blue)
5816       element = EL_CHAR_GREATER;
5817       break;
5818
5819     case 0x15d5:        // (blue)
5820       element = EL_CHAR_QUESTION;
5821       break;
5822
5823     case 0x15d6:        // (blue)
5824       element = EL_CHAR_COPYRIGHT;
5825       break;
5826
5827     case 0x15d7:        // (blue)
5828       element = EL_CHAR_UP;
5829       break;
5830
5831     case 0x15d8:        // (blue)
5832       element = EL_CHAR_DOWN;
5833       break;
5834
5835     case 0x15d9:        // (blue)
5836       element = EL_CHAR_BUTTON;
5837       break;
5838
5839     case 0x15da:        // (blue)
5840       element = EL_CHAR_PLUS;
5841       break;
5842
5843     case 0x15db:        // (blue)
5844       element = EL_CHAR_MINUS;
5845       break;
5846
5847     case 0x15dc:        // (blue)
5848       element = EL_CHAR_APOSTROPHE;
5849       break;
5850
5851     case 0x15dd:        // (blue)
5852       element = EL_CHAR_PARENLEFT;
5853       break;
5854
5855     case 0x15de:        // (blue)
5856       element = EL_CHAR_PARENRIGHT;
5857       break;
5858
5859     case 0x15df:        // (green)
5860       element = EL_CHAR_A;
5861       break;
5862
5863     case 0x15e0:        // (green)
5864       element = EL_CHAR_B;
5865       break;
5866
5867     case 0x15e1:        // (green)
5868       element = EL_CHAR_C;
5869       break;
5870
5871     case 0x15e2:        // (green)
5872       element = EL_CHAR_D;
5873       break;
5874
5875     case 0x15e3:        // (green)
5876       element = EL_CHAR_E;
5877       break;
5878
5879     case 0x15e4:        // (green)
5880       element = EL_CHAR_F;
5881       break;
5882
5883     case 0x15e5:        // (green)
5884       element = EL_CHAR_G;
5885       break;
5886
5887     case 0x15e6:        // (green)
5888       element = EL_CHAR_H;
5889       break;
5890
5891     case 0x15e7:        // (green)
5892       element = EL_CHAR_I;
5893       break;
5894
5895     case 0x15e8:        // (green)
5896       element = EL_CHAR_J;
5897       break;
5898
5899     case 0x15e9:        // (green)
5900       element = EL_CHAR_K;
5901       break;
5902
5903     case 0x15ea:        // (green)
5904       element = EL_CHAR_L;
5905       break;
5906
5907     case 0x15eb:        // (green)
5908       element = EL_CHAR_M;
5909       break;
5910
5911     case 0x15ec:        // (green)
5912       element = EL_CHAR_N;
5913       break;
5914
5915     case 0x15ed:        // (green)
5916       element = EL_CHAR_O;
5917       break;
5918
5919     case 0x15ee:        // (green)
5920       element = EL_CHAR_P;
5921       break;
5922
5923     case 0x15ef:        // (green)
5924       element = EL_CHAR_Q;
5925       break;
5926
5927     case 0x15f0:        // (green)
5928       element = EL_CHAR_R;
5929       break;
5930
5931     case 0x15f1:        // (green)
5932       element = EL_CHAR_S;
5933       break;
5934
5935     case 0x15f2:        // (green)
5936       element = EL_CHAR_T;
5937       break;
5938
5939     case 0x15f3:        // (green)
5940       element = EL_CHAR_U;
5941       break;
5942
5943     case 0x15f4:        // (green)
5944       element = EL_CHAR_V;
5945       break;
5946
5947     case 0x15f5:        // (green)
5948       element = EL_CHAR_W;
5949       break;
5950
5951     case 0x15f6:        // (green)
5952       element = EL_CHAR_X;
5953       break;
5954
5955     case 0x15f7:        // (green)
5956       element = EL_CHAR_Y;
5957       break;
5958
5959     case 0x15f8:        // (green)
5960       element = EL_CHAR_Z;
5961       break;
5962
5963     case 0x15f9:        // (green)
5964       element = EL_CHAR_AUMLAUT;
5965       break;
5966
5967     case 0x15fa:        // (green)
5968       element = EL_CHAR_OUMLAUT;
5969       break;
5970
5971     case 0x15fb:        // (green)
5972       element = EL_CHAR_UUMLAUT;
5973       break;
5974
5975     case 0x15fc:        // (green)
5976       element = EL_CHAR_0;
5977       break;
5978
5979     case 0x15fd:        // (green)
5980       element = EL_CHAR_1;
5981       break;
5982
5983     case 0x15fe:        // (green)
5984       element = EL_CHAR_2;
5985       break;
5986
5987     case 0x15ff:        // (green)
5988       element = EL_CHAR_3;
5989       break;
5990
5991     case 0x1600:        // (green)
5992       element = EL_CHAR_4;
5993       break;
5994
5995     case 0x1601:        // (green)
5996       element = EL_CHAR_5;
5997       break;
5998
5999     case 0x1602:        // (green)
6000       element = EL_CHAR_6;
6001       break;
6002
6003     case 0x1603:        // (green)
6004       element = EL_CHAR_7;
6005       break;
6006
6007     case 0x1604:        // (green)
6008       element = EL_CHAR_8;
6009       break;
6010
6011     case 0x1605:        // (green)
6012       element = EL_CHAR_9;
6013       break;
6014
6015     case 0x1606:        // (green)
6016       element = EL_CHAR_PERIOD;
6017       break;
6018
6019     case 0x1607:        // (green)
6020       element = EL_CHAR_EXCLAM;
6021       break;
6022
6023     case 0x1608:        // (green)
6024       element = EL_CHAR_COLON;
6025       break;
6026
6027     case 0x1609:        // (green)
6028       element = EL_CHAR_LESS;
6029       break;
6030
6031     case 0x160a:        // (green)
6032       element = EL_CHAR_GREATER;
6033       break;
6034
6035     case 0x160b:        // (green)
6036       element = EL_CHAR_QUESTION;
6037       break;
6038
6039     case 0x160c:        // (green)
6040       element = EL_CHAR_COPYRIGHT;
6041       break;
6042
6043     case 0x160d:        // (green)
6044       element = EL_CHAR_UP;
6045       break;
6046
6047     case 0x160e:        // (green)
6048       element = EL_CHAR_DOWN;
6049       break;
6050
6051     case 0x160f:        // (green)
6052       element = EL_CHAR_BUTTON;
6053       break;
6054
6055     case 0x1610:        // (green)
6056       element = EL_CHAR_PLUS;
6057       break;
6058
6059     case 0x1611:        // (green)
6060       element = EL_CHAR_MINUS;
6061       break;
6062
6063     case 0x1612:        // (green)
6064       element = EL_CHAR_APOSTROPHE;
6065       break;
6066
6067     case 0x1613:        // (green)
6068       element = EL_CHAR_PARENLEFT;
6069       break;
6070
6071     case 0x1614:        // (green)
6072       element = EL_CHAR_PARENRIGHT;
6073       break;
6074
6075     case 0x1615:        // (blue steel)
6076       element = EL_STEEL_CHAR_A;
6077       break;
6078
6079     case 0x1616:        // (blue steel)
6080       element = EL_STEEL_CHAR_B;
6081       break;
6082
6083     case 0x1617:        // (blue steel)
6084       element = EL_STEEL_CHAR_C;
6085       break;
6086
6087     case 0x1618:        // (blue steel)
6088       element = EL_STEEL_CHAR_D;
6089       break;
6090
6091     case 0x1619:        // (blue steel)
6092       element = EL_STEEL_CHAR_E;
6093       break;
6094
6095     case 0x161a:        // (blue steel)
6096       element = EL_STEEL_CHAR_F;
6097       break;
6098
6099     case 0x161b:        // (blue steel)
6100       element = EL_STEEL_CHAR_G;
6101       break;
6102
6103     case 0x161c:        // (blue steel)
6104       element = EL_STEEL_CHAR_H;
6105       break;
6106
6107     case 0x161d:        // (blue steel)
6108       element = EL_STEEL_CHAR_I;
6109       break;
6110
6111     case 0x161e:        // (blue steel)
6112       element = EL_STEEL_CHAR_J;
6113       break;
6114
6115     case 0x161f:        // (blue steel)
6116       element = EL_STEEL_CHAR_K;
6117       break;
6118
6119     case 0x1620:        // (blue steel)
6120       element = EL_STEEL_CHAR_L;
6121       break;
6122
6123     case 0x1621:        // (blue steel)
6124       element = EL_STEEL_CHAR_M;
6125       break;
6126
6127     case 0x1622:        // (blue steel)
6128       element = EL_STEEL_CHAR_N;
6129       break;
6130
6131     case 0x1623:        // (blue steel)
6132       element = EL_STEEL_CHAR_O;
6133       break;
6134
6135     case 0x1624:        // (blue steel)
6136       element = EL_STEEL_CHAR_P;
6137       break;
6138
6139     case 0x1625:        // (blue steel)
6140       element = EL_STEEL_CHAR_Q;
6141       break;
6142
6143     case 0x1626:        // (blue steel)
6144       element = EL_STEEL_CHAR_R;
6145       break;
6146
6147     case 0x1627:        // (blue steel)
6148       element = EL_STEEL_CHAR_S;
6149       break;
6150
6151     case 0x1628:        // (blue steel)
6152       element = EL_STEEL_CHAR_T;
6153       break;
6154
6155     case 0x1629:        // (blue steel)
6156       element = EL_STEEL_CHAR_U;
6157       break;
6158
6159     case 0x162a:        // (blue steel)
6160       element = EL_STEEL_CHAR_V;
6161       break;
6162
6163     case 0x162b:        // (blue steel)
6164       element = EL_STEEL_CHAR_W;
6165       break;
6166
6167     case 0x162c:        // (blue steel)
6168       element = EL_STEEL_CHAR_X;
6169       break;
6170
6171     case 0x162d:        // (blue steel)
6172       element = EL_STEEL_CHAR_Y;
6173       break;
6174
6175     case 0x162e:        // (blue steel)
6176       element = EL_STEEL_CHAR_Z;
6177       break;
6178
6179     case 0x162f:        // (blue steel)
6180       element = EL_STEEL_CHAR_AUMLAUT;
6181       break;
6182
6183     case 0x1630:        // (blue steel)
6184       element = EL_STEEL_CHAR_OUMLAUT;
6185       break;
6186
6187     case 0x1631:        // (blue steel)
6188       element = EL_STEEL_CHAR_UUMLAUT;
6189       break;
6190
6191     case 0x1632:        // (blue steel)
6192       element = EL_STEEL_CHAR_0;
6193       break;
6194
6195     case 0x1633:        // (blue steel)
6196       element = EL_STEEL_CHAR_1;
6197       break;
6198
6199     case 0x1634:        // (blue steel)
6200       element = EL_STEEL_CHAR_2;
6201       break;
6202
6203     case 0x1635:        // (blue steel)
6204       element = EL_STEEL_CHAR_3;
6205       break;
6206
6207     case 0x1636:        // (blue steel)
6208       element = EL_STEEL_CHAR_4;
6209       break;
6210
6211     case 0x1637:        // (blue steel)
6212       element = EL_STEEL_CHAR_5;
6213       break;
6214
6215     case 0x1638:        // (blue steel)
6216       element = EL_STEEL_CHAR_6;
6217       break;
6218
6219     case 0x1639:        // (blue steel)
6220       element = EL_STEEL_CHAR_7;
6221       break;
6222
6223     case 0x163a:        // (blue steel)
6224       element = EL_STEEL_CHAR_8;
6225       break;
6226
6227     case 0x163b:        // (blue steel)
6228       element = EL_STEEL_CHAR_9;
6229       break;
6230
6231     case 0x163c:        // (blue steel)
6232       element = EL_STEEL_CHAR_PERIOD;
6233       break;
6234
6235     case 0x163d:        // (blue steel)
6236       element = EL_STEEL_CHAR_EXCLAM;
6237       break;
6238
6239     case 0x163e:        // (blue steel)
6240       element = EL_STEEL_CHAR_COLON;
6241       break;
6242
6243     case 0x163f:        // (blue steel)
6244       element = EL_STEEL_CHAR_LESS;
6245       break;
6246
6247     case 0x1640:        // (blue steel)
6248       element = EL_STEEL_CHAR_GREATER;
6249       break;
6250
6251     case 0x1641:        // (blue steel)
6252       element = EL_STEEL_CHAR_QUESTION;
6253       break;
6254
6255     case 0x1642:        // (blue steel)
6256       element = EL_STEEL_CHAR_COPYRIGHT;
6257       break;
6258
6259     case 0x1643:        // (blue steel)
6260       element = EL_STEEL_CHAR_UP;
6261       break;
6262
6263     case 0x1644:        // (blue steel)
6264       element = EL_STEEL_CHAR_DOWN;
6265       break;
6266
6267     case 0x1645:        // (blue steel)
6268       element = EL_STEEL_CHAR_BUTTON;
6269       break;
6270
6271     case 0x1646:        // (blue steel)
6272       element = EL_STEEL_CHAR_PLUS;
6273       break;
6274
6275     case 0x1647:        // (blue steel)
6276       element = EL_STEEL_CHAR_MINUS;
6277       break;
6278
6279     case 0x1648:        // (blue steel)
6280       element = EL_STEEL_CHAR_APOSTROPHE;
6281       break;
6282
6283     case 0x1649:        // (blue steel)
6284       element = EL_STEEL_CHAR_PARENLEFT;
6285       break;
6286
6287     case 0x164a:        // (blue steel)
6288       element = EL_STEEL_CHAR_PARENRIGHT;
6289       break;
6290
6291     case 0x164b:        // (green steel)
6292       element = EL_STEEL_CHAR_A;
6293       break;
6294
6295     case 0x164c:        // (green steel)
6296       element = EL_STEEL_CHAR_B;
6297       break;
6298
6299     case 0x164d:        // (green steel)
6300       element = EL_STEEL_CHAR_C;
6301       break;
6302
6303     case 0x164e:        // (green steel)
6304       element = EL_STEEL_CHAR_D;
6305       break;
6306
6307     case 0x164f:        // (green steel)
6308       element = EL_STEEL_CHAR_E;
6309       break;
6310
6311     case 0x1650:        // (green steel)
6312       element = EL_STEEL_CHAR_F;
6313       break;
6314
6315     case 0x1651:        // (green steel)
6316       element = EL_STEEL_CHAR_G;
6317       break;
6318
6319     case 0x1652:        // (green steel)
6320       element = EL_STEEL_CHAR_H;
6321       break;
6322
6323     case 0x1653:        // (green steel)
6324       element = EL_STEEL_CHAR_I;
6325       break;
6326
6327     case 0x1654:        // (green steel)
6328       element = EL_STEEL_CHAR_J;
6329       break;
6330
6331     case 0x1655:        // (green steel)
6332       element = EL_STEEL_CHAR_K;
6333       break;
6334
6335     case 0x1656:        // (green steel)
6336       element = EL_STEEL_CHAR_L;
6337       break;
6338
6339     case 0x1657:        // (green steel)
6340       element = EL_STEEL_CHAR_M;
6341       break;
6342
6343     case 0x1658:        // (green steel)
6344       element = EL_STEEL_CHAR_N;
6345       break;
6346
6347     case 0x1659:        // (green steel)
6348       element = EL_STEEL_CHAR_O;
6349       break;
6350
6351     case 0x165a:        // (green steel)
6352       element = EL_STEEL_CHAR_P;
6353       break;
6354
6355     case 0x165b:        // (green steel)
6356       element = EL_STEEL_CHAR_Q;
6357       break;
6358
6359     case 0x165c:        // (green steel)
6360       element = EL_STEEL_CHAR_R;
6361       break;
6362
6363     case 0x165d:        // (green steel)
6364       element = EL_STEEL_CHAR_S;
6365       break;
6366
6367     case 0x165e:        // (green steel)
6368       element = EL_STEEL_CHAR_T;
6369       break;
6370
6371     case 0x165f:        // (green steel)
6372       element = EL_STEEL_CHAR_U;
6373       break;
6374
6375     case 0x1660:        // (green steel)
6376       element = EL_STEEL_CHAR_V;
6377       break;
6378
6379     case 0x1661:        // (green steel)
6380       element = EL_STEEL_CHAR_W;
6381       break;
6382
6383     case 0x1662:        // (green steel)
6384       element = EL_STEEL_CHAR_X;
6385       break;
6386
6387     case 0x1663:        // (green steel)
6388       element = EL_STEEL_CHAR_Y;
6389       break;
6390
6391     case 0x1664:        // (green steel)
6392       element = EL_STEEL_CHAR_Z;
6393       break;
6394
6395     case 0x1665:        // (green steel)
6396       element = EL_STEEL_CHAR_AUMLAUT;
6397       break;
6398
6399     case 0x1666:        // (green steel)
6400       element = EL_STEEL_CHAR_OUMLAUT;
6401       break;
6402
6403     case 0x1667:        // (green steel)
6404       element = EL_STEEL_CHAR_UUMLAUT;
6405       break;
6406
6407     case 0x1668:        // (green steel)
6408       element = EL_STEEL_CHAR_0;
6409       break;
6410
6411     case 0x1669:        // (green steel)
6412       element = EL_STEEL_CHAR_1;
6413       break;
6414
6415     case 0x166a:        // (green steel)
6416       element = EL_STEEL_CHAR_2;
6417       break;
6418
6419     case 0x166b:        // (green steel)
6420       element = EL_STEEL_CHAR_3;
6421       break;
6422
6423     case 0x166c:        // (green steel)
6424       element = EL_STEEL_CHAR_4;
6425       break;
6426
6427     case 0x166d:        // (green steel)
6428       element = EL_STEEL_CHAR_5;
6429       break;
6430
6431     case 0x166e:        // (green steel)
6432       element = EL_STEEL_CHAR_6;
6433       break;
6434
6435     case 0x166f:        // (green steel)
6436       element = EL_STEEL_CHAR_7;
6437       break;
6438
6439     case 0x1670:        // (green steel)
6440       element = EL_STEEL_CHAR_8;
6441       break;
6442
6443     case 0x1671:        // (green steel)
6444       element = EL_STEEL_CHAR_9;
6445       break;
6446
6447     case 0x1672:        // (green steel)
6448       element = EL_STEEL_CHAR_PERIOD;
6449       break;
6450
6451     case 0x1673:        // (green steel)
6452       element = EL_STEEL_CHAR_EXCLAM;
6453       break;
6454
6455     case 0x1674:        // (green steel)
6456       element = EL_STEEL_CHAR_COLON;
6457       break;
6458
6459     case 0x1675:        // (green steel)
6460       element = EL_STEEL_CHAR_LESS;
6461       break;
6462
6463     case 0x1676:        // (green steel)
6464       element = EL_STEEL_CHAR_GREATER;
6465       break;
6466
6467     case 0x1677:        // (green steel)
6468       element = EL_STEEL_CHAR_QUESTION;
6469       break;
6470
6471     case 0x1678:        // (green steel)
6472       element = EL_STEEL_CHAR_COPYRIGHT;
6473       break;
6474
6475     case 0x1679:        // (green steel)
6476       element = EL_STEEL_CHAR_UP;
6477       break;
6478
6479     case 0x167a:        // (green steel)
6480       element = EL_STEEL_CHAR_DOWN;
6481       break;
6482
6483     case 0x167b:        // (green steel)
6484       element = EL_STEEL_CHAR_BUTTON;
6485       break;
6486
6487     case 0x167c:        // (green steel)
6488       element = EL_STEEL_CHAR_PLUS;
6489       break;
6490
6491     case 0x167d:        // (green steel)
6492       element = EL_STEEL_CHAR_MINUS;
6493       break;
6494
6495     case 0x167e:        // (green steel)
6496       element = EL_STEEL_CHAR_APOSTROPHE;
6497       break;
6498
6499     case 0x167f:        // (green steel)
6500       element = EL_STEEL_CHAR_PARENLEFT;
6501       break;
6502
6503     case 0x1680:        // (green steel)
6504       element = EL_STEEL_CHAR_PARENRIGHT;
6505       break;
6506
6507     case 0x1681:        // gate (red)
6508       element = EL_EM_GATE_1;
6509       break;
6510
6511     case 0x1682:        // secret gate (red)
6512       element = EL_EM_GATE_1_GRAY;
6513       break;
6514
6515     case 0x1683:        // gate (yellow)
6516       element = EL_EM_GATE_2;
6517       break;
6518
6519     case 0x1684:        // secret gate (yellow)
6520       element = EL_EM_GATE_2_GRAY;
6521       break;
6522
6523     case 0x1685:        // gate (blue)
6524       element = EL_EM_GATE_4;
6525       break;
6526
6527     case 0x1686:        // secret gate (blue)
6528       element = EL_EM_GATE_4_GRAY;
6529       break;
6530
6531     case 0x1687:        // gate (green)
6532       element = EL_EM_GATE_3;
6533       break;
6534
6535     case 0x1688:        // secret gate (green)
6536       element = EL_EM_GATE_3_GRAY;
6537       break;
6538
6539     case 0x1689:        // gate (white)
6540       element = EL_DC_GATE_WHITE;
6541       break;
6542
6543     case 0x168a:        // secret gate (white)
6544       element = EL_DC_GATE_WHITE_GRAY;
6545       break;
6546
6547     case 0x168b:        // secret gate (no key)
6548       element = EL_DC_GATE_FAKE_GRAY;
6549       break;
6550
6551     case 0x168c:
6552       element = EL_ROBOT_WHEEL;
6553       break;
6554
6555     case 0x168d:
6556       element = EL_DC_TIMEGATE_SWITCH;
6557       break;
6558
6559     case 0x168e:
6560       element = EL_ACID_POOL_BOTTOM;
6561       break;
6562
6563     case 0x168f:
6564       element = EL_ACID_POOL_TOPLEFT;
6565       break;
6566
6567     case 0x1690:
6568       element = EL_ACID_POOL_TOPRIGHT;
6569       break;
6570
6571     case 0x1691:
6572       element = EL_ACID_POOL_BOTTOMLEFT;
6573       break;
6574
6575     case 0x1692:
6576       element = EL_ACID_POOL_BOTTOMRIGHT;
6577       break;
6578
6579     case 0x1693:
6580       element = EL_STEELWALL;
6581       break;
6582
6583     case 0x1694:
6584       element = EL_STEELWALL_SLIPPERY;
6585       break;
6586
6587     case 0x1695:        // steel wall (not round)
6588       element = EL_STEELWALL;
6589       break;
6590
6591     case 0x1696:        // steel wall (left)
6592       element = EL_DC_STEELWALL_1_LEFT;
6593       break;
6594
6595     case 0x1697:        // steel wall (bottom)
6596       element = EL_DC_STEELWALL_1_BOTTOM;
6597       break;
6598
6599     case 0x1698:        // steel wall (right)
6600       element = EL_DC_STEELWALL_1_RIGHT;
6601       break;
6602
6603     case 0x1699:        // steel wall (top)
6604       element = EL_DC_STEELWALL_1_TOP;
6605       break;
6606
6607     case 0x169a:        // steel wall (left/bottom)
6608       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6609       break;
6610
6611     case 0x169b:        // steel wall (right/bottom)
6612       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6613       break;
6614
6615     case 0x169c:        // steel wall (right/top)
6616       element = EL_DC_STEELWALL_1_TOPRIGHT;
6617       break;
6618
6619     case 0x169d:        // steel wall (left/top)
6620       element = EL_DC_STEELWALL_1_TOPLEFT;
6621       break;
6622
6623     case 0x169e:        // steel wall (right/bottom small)
6624       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6625       break;
6626
6627     case 0x169f:        // steel wall (left/bottom small)
6628       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6629       break;
6630
6631     case 0x16a0:        // steel wall (right/top small)
6632       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6633       break;
6634
6635     case 0x16a1:        // steel wall (left/top small)
6636       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6637       break;
6638
6639     case 0x16a2:        // steel wall (left/right)
6640       element = EL_DC_STEELWALL_1_VERTICAL;
6641       break;
6642
6643     case 0x16a3:        // steel wall (top/bottom)
6644       element = EL_DC_STEELWALL_1_HORIZONTAL;
6645       break;
6646
6647     case 0x16a4:        // steel wall 2 (left end)
6648       element = EL_DC_STEELWALL_2_LEFT;
6649       break;
6650
6651     case 0x16a5:        // steel wall 2 (right end)
6652       element = EL_DC_STEELWALL_2_RIGHT;
6653       break;
6654
6655     case 0x16a6:        // steel wall 2 (top end)
6656       element = EL_DC_STEELWALL_2_TOP;
6657       break;
6658
6659     case 0x16a7:        // steel wall 2 (bottom end)
6660       element = EL_DC_STEELWALL_2_BOTTOM;
6661       break;
6662
6663     case 0x16a8:        // steel wall 2 (left/right)
6664       element = EL_DC_STEELWALL_2_HORIZONTAL;
6665       break;
6666
6667     case 0x16a9:        // steel wall 2 (up/down)
6668       element = EL_DC_STEELWALL_2_VERTICAL;
6669       break;
6670
6671     case 0x16aa:        // steel wall 2 (mid)
6672       element = EL_DC_STEELWALL_2_MIDDLE;
6673       break;
6674
6675     case 0x16ab:
6676       element = EL_SIGN_EXCLAMATION;
6677       break;
6678
6679     case 0x16ac:
6680       element = EL_SIGN_RADIOACTIVITY;
6681       break;
6682
6683     case 0x16ad:
6684       element = EL_SIGN_STOP;
6685       break;
6686
6687     case 0x16ae:
6688       element = EL_SIGN_WHEELCHAIR;
6689       break;
6690
6691     case 0x16af:
6692       element = EL_SIGN_PARKING;
6693       break;
6694
6695     case 0x16b0:
6696       element = EL_SIGN_NO_ENTRY;
6697       break;
6698
6699     case 0x16b1:
6700       element = EL_SIGN_HEART;
6701       break;
6702
6703     case 0x16b2:
6704       element = EL_SIGN_GIVE_WAY;
6705       break;
6706
6707     case 0x16b3:
6708       element = EL_SIGN_ENTRY_FORBIDDEN;
6709       break;
6710
6711     case 0x16b4:
6712       element = EL_SIGN_EMERGENCY_EXIT;
6713       break;
6714
6715     case 0x16b5:
6716       element = EL_SIGN_YIN_YANG;
6717       break;
6718
6719     case 0x16b6:
6720       element = EL_WALL_EMERALD;
6721       break;
6722
6723     case 0x16b7:
6724       element = EL_WALL_DIAMOND;
6725       break;
6726
6727     case 0x16b8:
6728       element = EL_WALL_PEARL;
6729       break;
6730
6731     case 0x16b9:
6732       element = EL_WALL_CRYSTAL;
6733       break;
6734
6735     case 0x16ba:
6736       element = EL_INVISIBLE_WALL;
6737       break;
6738
6739     case 0x16bb:
6740       element = EL_INVISIBLE_STEELWALL;
6741       break;
6742
6743       // 0x16bc - 0x16cb:
6744       // EL_INVISIBLE_SAND
6745
6746     case 0x16cc:
6747       element = EL_LIGHT_SWITCH;
6748       break;
6749
6750     case 0x16cd:
6751       element = EL_ENVELOPE_1;
6752       break;
6753
6754     default:
6755       if (element >= 0x0117 && element <= 0x036e)       // (?)
6756         element = EL_DIAMOND;
6757       else if (element >= 0x042d && element <= 0x0684)  // (?)
6758         element = EL_EMERALD;
6759       else if (element >= 0x157c && element <= 0x158b)
6760         element = EL_SAND;
6761       else if (element >= 0x1590 && element <= 0x159f)
6762         element = EL_DC_LANDMINE;
6763       else if (element >= 0x16bc && element <= 0x16cb)
6764         element = EL_INVISIBLE_SAND;
6765       else
6766       {
6767         Warn("unknown Diamond Caves element 0x%04x", element);
6768
6769         element = EL_UNKNOWN;
6770       }
6771       break;
6772   }
6773
6774   return getMappedElement(element);
6775 }
6776
6777 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6778 {
6779   byte header[DC_LEVEL_HEADER_SIZE];
6780   int envelope_size;
6781   int envelope_header_pos = 62;
6782   int envelope_content_pos = 94;
6783   int level_name_pos = 251;
6784   int level_author_pos = 292;
6785   int envelope_header_len;
6786   int envelope_content_len;
6787   int level_name_len;
6788   int level_author_len;
6789   int fieldx, fieldy;
6790   int num_yamyam_contents;
6791   int i, x, y;
6792
6793   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6794
6795   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6796   {
6797     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6798
6799     header[i * 2 + 0] = header_word >> 8;
6800     header[i * 2 + 1] = header_word & 0xff;
6801   }
6802
6803   // read some values from level header to check level decoding integrity
6804   fieldx = header[6] | (header[7] << 8);
6805   fieldy = header[8] | (header[9] << 8);
6806   num_yamyam_contents = header[60] | (header[61] << 8);
6807
6808   // do some simple sanity checks to ensure that level was correctly decoded
6809   if (fieldx < 1 || fieldx > 256 ||
6810       fieldy < 1 || fieldy > 256 ||
6811       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6812   {
6813     level->no_valid_file = TRUE;
6814
6815     Warn("cannot decode level from stream -- using empty level");
6816
6817     return;
6818   }
6819
6820   // maximum envelope header size is 31 bytes
6821   envelope_header_len   = header[envelope_header_pos];
6822   // maximum envelope content size is 110 (156?) bytes
6823   envelope_content_len  = header[envelope_content_pos];
6824
6825   // maximum level title size is 40 bytes
6826   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6827   // maximum level author size is 30 (51?) bytes
6828   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6829
6830   envelope_size = 0;
6831
6832   for (i = 0; i < envelope_header_len; i++)
6833     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6834       level->envelope[0].text[envelope_size++] =
6835         header[envelope_header_pos + 1 + i];
6836
6837   if (envelope_header_len > 0 && envelope_content_len > 0)
6838   {
6839     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6840       level->envelope[0].text[envelope_size++] = '\n';
6841     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6842       level->envelope[0].text[envelope_size++] = '\n';
6843   }
6844
6845   for (i = 0; i < envelope_content_len; i++)
6846     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6847       level->envelope[0].text[envelope_size++] =
6848         header[envelope_content_pos + 1 + i];
6849
6850   level->envelope[0].text[envelope_size] = '\0';
6851
6852   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6853   level->envelope[0].ysize = 10;
6854   level->envelope[0].autowrap = TRUE;
6855   level->envelope[0].centered = TRUE;
6856
6857   for (i = 0; i < level_name_len; i++)
6858     level->name[i] = header[level_name_pos + 1 + i];
6859   level->name[level_name_len] = '\0';
6860
6861   for (i = 0; i < level_author_len; i++)
6862     level->author[i] = header[level_author_pos + 1 + i];
6863   level->author[level_author_len] = '\0';
6864
6865   num_yamyam_contents = header[60] | (header[61] << 8);
6866   level->num_yamyam_contents =
6867     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6868
6869   for (i = 0; i < num_yamyam_contents; i++)
6870   {
6871     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6872     {
6873       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6874       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6875
6876       if (i < MAX_ELEMENT_CONTENTS)
6877         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6878     }
6879   }
6880
6881   fieldx = header[6] | (header[7] << 8);
6882   fieldy = header[8] | (header[9] << 8);
6883   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6884   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6885
6886   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6887   {
6888     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6889     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6890
6891     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6892       level->field[x][y] = getMappedElement_DC(element_dc);
6893   }
6894
6895   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6896   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6897   level->field[x][y] = EL_PLAYER_1;
6898
6899   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6900   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6901   level->field[x][y] = EL_PLAYER_2;
6902
6903   level->gems_needed            = header[18] | (header[19] << 8);
6904
6905   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6906   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6907   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6908   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6909   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6910   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6911   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6912   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6913   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6914   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6915   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6916   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6917
6918   level->time                   = header[44] | (header[45] << 8);
6919
6920   level->amoeba_speed           = header[46] | (header[47] << 8);
6921   level->time_light             = header[48] | (header[49] << 8);
6922   level->time_timegate          = header[50] | (header[51] << 8);
6923   level->time_wheel             = header[52] | (header[53] << 8);
6924   level->time_magic_wall        = header[54] | (header[55] << 8);
6925   level->extra_time             = header[56] | (header[57] << 8);
6926   level->shield_normal_time     = header[58] | (header[59] << 8);
6927
6928   // shield and extra time elements do not have a score
6929   level->score[SC_SHIELD]       = 0;
6930   level->extra_time_score       = 0;
6931
6932   // set time for normal and deadly shields to the same value
6933   level->shield_deadly_time     = level->shield_normal_time;
6934
6935   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6936   // can slip down from flat walls, like normal walls and steel walls
6937   level->em_slippery_gems = TRUE;
6938
6939   // time score is counted for each 10 seconds left in Diamond Caves levels
6940   level->time_score_base = 10;
6941 }
6942
6943 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6944                                      struct LevelFileInfo *level_file_info,
6945                                      boolean level_info_only)
6946 {
6947   char *filename = level_file_info->filename;
6948   File *file;
6949   int num_magic_bytes = 8;
6950   char magic_bytes[num_magic_bytes + 1];
6951   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6952
6953   if (!(file = openFile(filename, MODE_READ)))
6954   {
6955     level->no_valid_file = TRUE;
6956
6957     if (!level_info_only)
6958       Warn("cannot read level '%s' -- using empty level", filename);
6959
6960     return;
6961   }
6962
6963   // fseek(file, 0x0000, SEEK_SET);
6964
6965   if (level_file_info->packed)
6966   {
6967     // read "magic bytes" from start of file
6968     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6969       magic_bytes[0] = '\0';
6970
6971     // check "magic bytes" for correct file format
6972     if (!strPrefix(magic_bytes, "DC2"))
6973     {
6974       level->no_valid_file = TRUE;
6975
6976       Warn("unknown DC level file '%s' -- using empty level", filename);
6977
6978       return;
6979     }
6980
6981     if (strPrefix(magic_bytes, "DC2Win95") ||
6982         strPrefix(magic_bytes, "DC2Win98"))
6983     {
6984       int position_first_level = 0x00fa;
6985       int extra_bytes = 4;
6986       int skip_bytes;
6987
6988       // advance file stream to first level inside the level package
6989       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6990
6991       // each block of level data is followed by block of non-level data
6992       num_levels_to_skip *= 2;
6993
6994       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6995       while (num_levels_to_skip >= 0)
6996       {
6997         // advance file stream to next level inside the level package
6998         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6999         {
7000           level->no_valid_file = TRUE;
7001
7002           Warn("cannot fseek in file '%s' -- using empty level", filename);
7003
7004           return;
7005         }
7006
7007         // skip apparently unused extra bytes following each level
7008         ReadUnusedBytesFromFile(file, extra_bytes);
7009
7010         // read size of next level in level package
7011         skip_bytes = getFile32BitLE(file);
7012
7013         num_levels_to_skip--;
7014       }
7015     }
7016     else
7017     {
7018       level->no_valid_file = TRUE;
7019
7020       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7021
7022       return;
7023     }
7024   }
7025
7026   LoadLevelFromFileStream_DC(file, level);
7027
7028   closeFile(file);
7029 }
7030
7031
7032 // ----------------------------------------------------------------------------
7033 // functions for loading SB level
7034 // ----------------------------------------------------------------------------
7035
7036 int getMappedElement_SB(int element_ascii, boolean use_ces)
7037 {
7038   static struct
7039   {
7040     int ascii;
7041     int sb;
7042     int ce;
7043   }
7044   sb_element_mapping[] =
7045   {
7046     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7047     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7048     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7049     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7050     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7051     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7052     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7053     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7054
7055     { 0,   -1,                      -1          },
7056   };
7057
7058   int i;
7059
7060   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7061     if (element_ascii == sb_element_mapping[i].ascii)
7062       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7063
7064   return EL_UNDEFINED;
7065 }
7066
7067 static void SetLevelSettings_SB(struct LevelInfo *level)
7068 {
7069   // time settings
7070   level->time = 0;
7071   level->use_step_counter = TRUE;
7072
7073   // score settings
7074   level->score[SC_TIME_BONUS] = 0;
7075   level->time_score_base = 1;
7076   level->rate_time_over_score = TRUE;
7077
7078   // game settings
7079   level->auto_exit_sokoban = TRUE;
7080 }
7081
7082 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7083                                      struct LevelFileInfo *level_file_info,
7084                                      boolean level_info_only)
7085 {
7086   char *filename = level_file_info->filename;
7087   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7088   char last_comment[MAX_LINE_LEN];
7089   char level_name[MAX_LINE_LEN];
7090   char *line_ptr;
7091   File *file;
7092   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7093   boolean read_continued_line = FALSE;
7094   boolean reading_playfield = FALSE;
7095   boolean got_valid_playfield_line = FALSE;
7096   boolean invalid_playfield_char = FALSE;
7097   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7098   int file_level_nr = 0;
7099   int x = 0, y = 0;             // initialized to make compilers happy
7100
7101   last_comment[0] = '\0';
7102   level_name[0] = '\0';
7103
7104   if (!(file = openFile(filename, MODE_READ)))
7105   {
7106     level->no_valid_file = TRUE;
7107
7108     if (!level_info_only)
7109       Warn("cannot read level '%s' -- using empty level", filename);
7110
7111     return;
7112   }
7113
7114   while (!checkEndOfFile(file))
7115   {
7116     // level successfully read, but next level may follow here
7117     if (!got_valid_playfield_line && reading_playfield)
7118     {
7119       // read playfield from single level file -- skip remaining file
7120       if (!level_file_info->packed)
7121         break;
7122
7123       if (file_level_nr >= num_levels_to_skip)
7124         break;
7125
7126       file_level_nr++;
7127
7128       last_comment[0] = '\0';
7129       level_name[0] = '\0';
7130
7131       reading_playfield = FALSE;
7132     }
7133
7134     got_valid_playfield_line = FALSE;
7135
7136     // read next line of input file
7137     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7138       break;
7139
7140     // cut trailing line break (this can be newline and/or carriage return)
7141     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7142       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7143         *line_ptr = '\0';
7144
7145     // copy raw input line for later use (mainly debugging output)
7146     strcpy(line_raw, line);
7147
7148     if (read_continued_line)
7149     {
7150       // append new line to existing line, if there is enough space
7151       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7152         strcat(previous_line, line_ptr);
7153
7154       strcpy(line, previous_line);      // copy storage buffer to line
7155
7156       read_continued_line = FALSE;
7157     }
7158
7159     // if the last character is '\', continue at next line
7160     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7161     {
7162       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7163       strcpy(previous_line, line);      // copy line to storage buffer
7164
7165       read_continued_line = TRUE;
7166
7167       continue;
7168     }
7169
7170     // skip empty lines
7171     if (line[0] == '\0')
7172       continue;
7173
7174     // extract comment text from comment line
7175     if (line[0] == ';')
7176     {
7177       for (line_ptr = line; *line_ptr; line_ptr++)
7178         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7179           break;
7180
7181       strcpy(last_comment, line_ptr);
7182
7183       continue;
7184     }
7185
7186     // extract level title text from line containing level title
7187     if (line[0] == '\'')
7188     {
7189       strcpy(level_name, &line[1]);
7190
7191       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7192         level_name[strlen(level_name) - 1] = '\0';
7193
7194       continue;
7195     }
7196
7197     // skip lines containing only spaces (or empty lines)
7198     for (line_ptr = line; *line_ptr; line_ptr++)
7199       if (*line_ptr != ' ')
7200         break;
7201     if (*line_ptr == '\0')
7202       continue;
7203
7204     // at this point, we have found a line containing part of a playfield
7205
7206     got_valid_playfield_line = TRUE;
7207
7208     if (!reading_playfield)
7209     {
7210       reading_playfield = TRUE;
7211       invalid_playfield_char = FALSE;
7212
7213       for (x = 0; x < MAX_LEV_FIELDX; x++)
7214         for (y = 0; y < MAX_LEV_FIELDY; y++)
7215           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7216
7217       level->fieldx = 0;
7218       level->fieldy = 0;
7219
7220       // start with topmost tile row
7221       y = 0;
7222     }
7223
7224     // skip playfield line if larger row than allowed
7225     if (y >= MAX_LEV_FIELDY)
7226       continue;
7227
7228     // start with leftmost tile column
7229     x = 0;
7230
7231     // read playfield elements from line
7232     for (line_ptr = line; *line_ptr; line_ptr++)
7233     {
7234       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7235
7236       // stop parsing playfield line if larger column than allowed
7237       if (x >= MAX_LEV_FIELDX)
7238         break;
7239
7240       if (mapped_sb_element == EL_UNDEFINED)
7241       {
7242         invalid_playfield_char = TRUE;
7243
7244         break;
7245       }
7246
7247       level->field[x][y] = mapped_sb_element;
7248
7249       // continue with next tile column
7250       x++;
7251
7252       level->fieldx = MAX(x, level->fieldx);
7253     }
7254
7255     if (invalid_playfield_char)
7256     {
7257       // if first playfield line, treat invalid lines as comment lines
7258       if (y == 0)
7259         reading_playfield = FALSE;
7260
7261       continue;
7262     }
7263
7264     // continue with next tile row
7265     y++;
7266   }
7267
7268   closeFile(file);
7269
7270   level->fieldy = y;
7271
7272   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7273   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7274
7275   if (!reading_playfield)
7276   {
7277     level->no_valid_file = TRUE;
7278
7279     Warn("cannot read level '%s' -- using empty level", filename);
7280
7281     return;
7282   }
7283
7284   if (*level_name != '\0')
7285   {
7286     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7287     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7288   }
7289   else if (*last_comment != '\0')
7290   {
7291     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7292     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7293   }
7294   else
7295   {
7296     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7297   }
7298
7299   // set all empty fields beyond the border walls to invisible steel wall
7300   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7301   {
7302     if ((x == 0 || x == level->fieldx - 1 ||
7303          y == 0 || y == level->fieldy - 1) &&
7304         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7305       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7306                      level->field, level->fieldx, level->fieldy);
7307   }
7308
7309   // set special level settings for Sokoban levels
7310   SetLevelSettings_SB(level);
7311
7312   if (load_xsb_to_ces)
7313   {
7314     // special global settings can now be set in level template
7315     level->use_custom_template = TRUE;
7316   }
7317 }
7318
7319
7320 // -------------------------------------------------------------------------
7321 // functions for handling native levels
7322 // -------------------------------------------------------------------------
7323
7324 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7325                                      struct LevelFileInfo *level_file_info,
7326                                      boolean level_info_only)
7327 {
7328   int pos = 0;
7329
7330   // determine position of requested level inside level package
7331   if (level_file_info->packed)
7332     pos = level_file_info->nr - leveldir_current->first_level;
7333
7334   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7335     level->no_valid_file = TRUE;
7336 }
7337
7338 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7339                                      struct LevelFileInfo *level_file_info,
7340                                      boolean level_info_only)
7341 {
7342   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7343     level->no_valid_file = TRUE;
7344 }
7345
7346 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7347                                      struct LevelFileInfo *level_file_info,
7348                                      boolean level_info_only)
7349 {
7350   int pos = 0;
7351
7352   // determine position of requested level inside level package
7353   if (level_file_info->packed)
7354     pos = level_file_info->nr - leveldir_current->first_level;
7355
7356   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7357     level->no_valid_file = TRUE;
7358 }
7359
7360 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7361                                      struct LevelFileInfo *level_file_info,
7362                                      boolean level_info_only)
7363 {
7364   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7365     level->no_valid_file = TRUE;
7366 }
7367
7368 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7369 {
7370   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7371     CopyNativeLevel_RND_to_BD(level);
7372   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7373     CopyNativeLevel_RND_to_EM(level);
7374   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7375     CopyNativeLevel_RND_to_SP(level);
7376   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7377     CopyNativeLevel_RND_to_MM(level);
7378 }
7379
7380 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7381 {
7382   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7383     CopyNativeLevel_BD_to_RND(level);
7384   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7385     CopyNativeLevel_EM_to_RND(level);
7386   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7387     CopyNativeLevel_SP_to_RND(level);
7388   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7389     CopyNativeLevel_MM_to_RND(level);
7390 }
7391
7392 void SaveNativeLevel(struct LevelInfo *level)
7393 {
7394   // saving native level files only supported for some game engines
7395   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7396       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7397     return;
7398
7399   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7400                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7401   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7402   char *filename = getLevelFilenameFromBasename(basename);
7403
7404   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7405     return;
7406
7407   boolean success = FALSE;
7408
7409   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7410   {
7411     CopyNativeLevel_RND_to_BD(level);
7412     // CopyNativeTape_RND_to_BD(level);
7413
7414     success = SaveNativeLevel_BD(filename);
7415   }
7416   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7417   {
7418     CopyNativeLevel_RND_to_SP(level);
7419     CopyNativeTape_RND_to_SP(level);
7420
7421     success = SaveNativeLevel_SP(filename);
7422   }
7423
7424   if (success)
7425     Request("Native level file saved!", REQ_CONFIRM);
7426   else
7427     Request("Failed to save native level file!", REQ_CONFIRM);
7428 }
7429
7430
7431 // ----------------------------------------------------------------------------
7432 // functions for loading generic level
7433 // ----------------------------------------------------------------------------
7434
7435 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7436                                   struct LevelFileInfo *level_file_info,
7437                                   boolean level_info_only)
7438 {
7439   // always start with reliable default values
7440   setLevelInfoToDefaults(level, level_info_only, TRUE);
7441
7442   switch (level_file_info->type)
7443   {
7444     case LEVEL_FILE_TYPE_RND:
7445       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7446       break;
7447
7448     case LEVEL_FILE_TYPE_BD:
7449       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7450       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7451       break;
7452
7453     case LEVEL_FILE_TYPE_EM:
7454       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7455       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7456       break;
7457
7458     case LEVEL_FILE_TYPE_SP:
7459       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7460       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7461       break;
7462
7463     case LEVEL_FILE_TYPE_MM:
7464       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7465       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7466       break;
7467
7468     case LEVEL_FILE_TYPE_DC:
7469       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7470       break;
7471
7472     case LEVEL_FILE_TYPE_SB:
7473       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7474       break;
7475
7476     default:
7477       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7478       break;
7479   }
7480
7481   // if level file is invalid, restore level structure to default values
7482   if (level->no_valid_file)
7483     setLevelInfoToDefaults(level, level_info_only, FALSE);
7484
7485   if (check_special_flags("use_native_bd_game_engine"))
7486     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7487
7488   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7489     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7490
7491   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7492     CopyNativeLevel_Native_to_RND(level);
7493 }
7494
7495 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7496 {
7497   static struct LevelFileInfo level_file_info;
7498
7499   // always start with reliable default values
7500   setFileInfoToDefaults(&level_file_info);
7501
7502   level_file_info.nr = 0;                       // unknown level number
7503   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7504
7505   setString(&level_file_info.filename, filename);
7506
7507   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7508 }
7509
7510 static void LoadLevel_InitVersion(struct LevelInfo *level)
7511 {
7512   int i, j;
7513
7514   if (leveldir_current == NULL)         // only when dumping level
7515     return;
7516
7517   // all engine modifications also valid for levels which use latest engine
7518   if (level->game_version < VERSION_IDENT(3,2,0,5))
7519   {
7520     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7521     level->time_score_base = 10;
7522   }
7523
7524   if (leveldir_current->latest_engine)
7525   {
7526     // ---------- use latest game engine --------------------------------------
7527
7528     /* For all levels which are forced to use the latest game engine version
7529        (normally all but user contributed, private and undefined levels), set
7530        the game engine version to the actual version; this allows for actual
7531        corrections in the game engine to take effect for existing, converted
7532        levels (from "classic" or other existing games) to make the emulation
7533        of the corresponding game more accurate, while (hopefully) not breaking
7534        existing levels created from other players. */
7535
7536     level->game_version = GAME_VERSION_ACTUAL;
7537
7538     /* Set special EM style gems behaviour: EM style gems slip down from
7539        normal, steel and growing wall. As this is a more fundamental change,
7540        it seems better to set the default behaviour to "off" (as it is more
7541        natural) and make it configurable in the level editor (as a property
7542        of gem style elements). Already existing converted levels (neither
7543        private nor contributed levels) are changed to the new behaviour. */
7544
7545     if (level->file_version < FILE_VERSION_2_0)
7546       level->em_slippery_gems = TRUE;
7547
7548     return;
7549   }
7550
7551   // ---------- use game engine the level was created with --------------------
7552
7553   /* For all levels which are not forced to use the latest game engine
7554      version (normally user contributed, private and undefined levels),
7555      use the version of the game engine the levels were created for.
7556
7557      Since 2.0.1, the game engine version is now directly stored
7558      in the level file (chunk "VERS"), so there is no need anymore
7559      to set the game version from the file version (except for old,
7560      pre-2.0 levels, where the game version is still taken from the
7561      file format version used to store the level -- see above). */
7562
7563   // player was faster than enemies in 1.0.0 and before
7564   if (level->file_version == FILE_VERSION_1_0)
7565     for (i = 0; i < MAX_PLAYERS; i++)
7566       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7567
7568   // default behaviour for EM style gems was "slippery" only in 2.0.1
7569   if (level->game_version == VERSION_IDENT(2,0,1,0))
7570     level->em_slippery_gems = TRUE;
7571
7572   // springs could be pushed over pits before (pre-release version) 2.2.0
7573   if (level->game_version < VERSION_IDENT(2,2,0,0))
7574     level->use_spring_bug = TRUE;
7575
7576   if (level->game_version < VERSION_IDENT(3,2,0,5))
7577   {
7578     // time orb caused limited time in endless time levels before 3.2.0-5
7579     level->use_time_orb_bug = TRUE;
7580
7581     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7582     level->block_snap_field = FALSE;
7583
7584     // extra time score was same value as time left score before 3.2.0-5
7585     level->extra_time_score = level->score[SC_TIME_BONUS];
7586   }
7587
7588   if (level->game_version < VERSION_IDENT(3,2,0,7))
7589   {
7590     // default behaviour for snapping was "not continuous" before 3.2.0-7
7591     level->continuous_snapping = FALSE;
7592   }
7593
7594   // only few elements were able to actively move into acid before 3.1.0
7595   // trigger settings did not exist before 3.1.0; set to default "any"
7596   if (level->game_version < VERSION_IDENT(3,1,0,0))
7597   {
7598     // correct "can move into acid" settings (all zero in old levels)
7599
7600     level->can_move_into_acid_bits = 0; // nothing can move into acid
7601     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7602
7603     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7604     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7605     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7606     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7607
7608     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7609       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7610
7611     // correct trigger settings (stored as zero == "none" in old levels)
7612
7613     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7614     {
7615       int element = EL_CUSTOM_START + i;
7616       struct ElementInfo *ei = &element_info[element];
7617
7618       for (j = 0; j < ei->num_change_pages; j++)
7619       {
7620         struct ElementChangeInfo *change = &ei->change_page[j];
7621
7622         change->trigger_player = CH_PLAYER_ANY;
7623         change->trigger_page = CH_PAGE_ANY;
7624       }
7625     }
7626   }
7627
7628   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7629   {
7630     int element = EL_CUSTOM_256;
7631     struct ElementInfo *ei = &element_info[element];
7632     struct ElementChangeInfo *change = &ei->change_page[0];
7633
7634     /* This is needed to fix a problem that was caused by a bugfix in function
7635        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7636        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7637        not replace walkable elements, but instead just placed the player on it,
7638        without placing the Sokoban field under the player). Unfortunately, this
7639        breaks "Snake Bite" style levels when the snake is halfway through a door
7640        that just closes (the snake head is still alive and can be moved in this
7641        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7642        player (without Sokoban element) which then gets killed as designed). */
7643
7644     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7645          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7646         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7647       change->target_element = EL_PLAYER_1;
7648   }
7649
7650   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7651   if (level->game_version < VERSION_IDENT(3,2,5,0))
7652   {
7653     /* This is needed to fix a problem that was caused by a bugfix in function
7654        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7655        corrects the behaviour when a custom element changes to another custom
7656        element with a higher element number that has change actions defined.
7657        Normally, only one change per frame is allowed for custom elements.
7658        Therefore, it is checked if a custom element already changed in the
7659        current frame; if it did, subsequent changes are suppressed.
7660        Unfortunately, this is only checked for element changes, but not for
7661        change actions, which are still executed. As the function above loops
7662        through all custom elements from lower to higher, an element change
7663        resulting in a lower CE number won't be checked again, while a target
7664        element with a higher number will also be checked, and potential change
7665        actions will get executed for this CE, too (which is wrong), while
7666        further changes are ignored (which is correct). As this bugfix breaks
7667        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7668        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7669        behaviour for existing levels and tapes that make use of this bug */
7670
7671     level->use_action_after_change_bug = TRUE;
7672   }
7673
7674   // not centering level after relocating player was default only in 3.2.3
7675   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7676     level->shifted_relocation = TRUE;
7677
7678   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7679   if (level->game_version < VERSION_IDENT(3,2,6,0))
7680     level->em_explodes_by_fire = TRUE;
7681
7682   // levels were solved by the first player entering an exit up to 4.1.0.0
7683   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7684     level->solved_by_one_player = TRUE;
7685
7686   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7687   if (level->game_version < VERSION_IDENT(4,1,1,1))
7688     level->use_life_bugs = TRUE;
7689
7690   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7691   if (level->game_version < VERSION_IDENT(4,1,1,1))
7692     level->sb_objects_needed = FALSE;
7693
7694   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7695   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7696     level->finish_dig_collect = FALSE;
7697
7698   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7699   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7700     level->keep_walkable_ce = TRUE;
7701 }
7702
7703 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7704 {
7705   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7706   int x, y;
7707
7708   // check if this level is (not) a Sokoban level
7709   for (y = 0; y < level->fieldy; y++)
7710     for (x = 0; x < level->fieldx; x++)
7711       if (!IS_SB_ELEMENT(Tile[x][y]))
7712         is_sokoban_level = FALSE;
7713
7714   if (is_sokoban_level)
7715   {
7716     // set special level settings for Sokoban levels
7717     SetLevelSettings_SB(level);
7718   }
7719 }
7720
7721 static void LoadLevel_InitSettings(struct LevelInfo *level)
7722 {
7723   // adjust level settings for (non-native) Sokoban-style levels
7724   LoadLevel_InitSettings_SB(level);
7725
7726   // rename levels with title "nameless level" or if renaming is forced
7727   if (leveldir_current->empty_level_name != NULL &&
7728       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7729        leveldir_current->force_level_name))
7730     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7731              leveldir_current->empty_level_name, level_nr);
7732 }
7733
7734 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7735 {
7736   int i, x, y;
7737
7738   // map elements that have changed in newer versions
7739   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7740                                                     level->game_version);
7741   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7742     for (x = 0; x < 3; x++)
7743       for (y = 0; y < 3; y++)
7744         level->yamyam_content[i].e[x][y] =
7745           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7746                                     level->game_version);
7747
7748 }
7749
7750 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7751 {
7752   int i, j;
7753
7754   // map custom element change events that have changed in newer versions
7755   // (these following values were accidentally changed in version 3.0.1)
7756   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7757   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7758   {
7759     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7760     {
7761       int element = EL_CUSTOM_START + i;
7762
7763       // order of checking and copying events to be mapped is important
7764       // (do not change the start and end value -- they are constant)
7765       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7766       {
7767         if (HAS_CHANGE_EVENT(element, j - 2))
7768         {
7769           SET_CHANGE_EVENT(element, j - 2, FALSE);
7770           SET_CHANGE_EVENT(element, j, TRUE);
7771         }
7772       }
7773
7774       // order of checking and copying events to be mapped is important
7775       // (do not change the start and end value -- they are constant)
7776       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7777       {
7778         if (HAS_CHANGE_EVENT(element, j - 1))
7779         {
7780           SET_CHANGE_EVENT(element, j - 1, FALSE);
7781           SET_CHANGE_EVENT(element, j, TRUE);
7782         }
7783       }
7784     }
7785   }
7786
7787   // initialize "can_change" field for old levels with only one change page
7788   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7789   {
7790     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7791     {
7792       int element = EL_CUSTOM_START + i;
7793
7794       if (CAN_CHANGE(element))
7795         element_info[element].change->can_change = TRUE;
7796     }
7797   }
7798
7799   // correct custom element values (for old levels without these options)
7800   if (level->game_version < VERSION_IDENT(3,1,1,0))
7801   {
7802     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7803     {
7804       int element = EL_CUSTOM_START + i;
7805       struct ElementInfo *ei = &element_info[element];
7806
7807       if (ei->access_direction == MV_NO_DIRECTION)
7808         ei->access_direction = MV_ALL_DIRECTIONS;
7809     }
7810   }
7811
7812   // correct custom element values (fix invalid values for all versions)
7813   if (1)
7814   {
7815     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7816     {
7817       int element = EL_CUSTOM_START + i;
7818       struct ElementInfo *ei = &element_info[element];
7819
7820       for (j = 0; j < ei->num_change_pages; j++)
7821       {
7822         struct ElementChangeInfo *change = &ei->change_page[j];
7823
7824         if (change->trigger_player == CH_PLAYER_NONE)
7825           change->trigger_player = CH_PLAYER_ANY;
7826
7827         if (change->trigger_side == CH_SIDE_NONE)
7828           change->trigger_side = CH_SIDE_ANY;
7829       }
7830     }
7831   }
7832
7833   // initialize "can_explode" field for old levels which did not store this
7834   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7835   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7836   {
7837     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7838     {
7839       int element = EL_CUSTOM_START + i;
7840
7841       if (EXPLODES_1X1_OLD(element))
7842         element_info[element].explosion_type = EXPLODES_1X1;
7843
7844       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7845                                              EXPLODES_SMASHED(element) ||
7846                                              EXPLODES_IMPACT(element)));
7847     }
7848   }
7849
7850   // correct previously hard-coded move delay values for maze runner style
7851   if (level->game_version < VERSION_IDENT(3,1,1,0))
7852   {
7853     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7854     {
7855       int element = EL_CUSTOM_START + i;
7856
7857       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7858       {
7859         // previously hard-coded and therefore ignored
7860         element_info[element].move_delay_fixed = 9;
7861         element_info[element].move_delay_random = 0;
7862       }
7863     }
7864   }
7865
7866   // set some other uninitialized values of custom elements in older levels
7867   if (level->game_version < VERSION_IDENT(3,1,0,0))
7868   {
7869     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7870     {
7871       int element = EL_CUSTOM_START + i;
7872
7873       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7874
7875       element_info[element].explosion_delay = 17;
7876       element_info[element].ignition_delay = 8;
7877     }
7878   }
7879
7880   // set mouse click change events to work for left/middle/right mouse button
7881   if (level->game_version < VERSION_IDENT(4,2,3,0))
7882   {
7883     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7884     {
7885       int element = EL_CUSTOM_START + i;
7886       struct ElementInfo *ei = &element_info[element];
7887
7888       for (j = 0; j < ei->num_change_pages; j++)
7889       {
7890         struct ElementChangeInfo *change = &ei->change_page[j];
7891
7892         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7893             change->has_event[CE_PRESSED_BY_MOUSE] ||
7894             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7895             change->has_event[CE_MOUSE_PRESSED_ON_X])
7896           change->trigger_side = CH_SIDE_ANY;
7897       }
7898     }
7899   }
7900 }
7901
7902 static void LoadLevel_InitElements(struct LevelInfo *level)
7903 {
7904   LoadLevel_InitStandardElements(level);
7905
7906   if (level->file_has_custom_elements)
7907     LoadLevel_InitCustomElements(level);
7908
7909   // initialize element properties for level editor etc.
7910   InitElementPropertiesEngine(level->game_version);
7911   InitElementPropertiesGfxElement();
7912 }
7913
7914 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7915 {
7916   int x, y;
7917
7918   // map elements that have changed in newer versions
7919   for (y = 0; y < level->fieldy; y++)
7920     for (x = 0; x < level->fieldx; x++)
7921       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7922                                                      level->game_version);
7923
7924   // clear unused playfield data (nicer if level gets resized in editor)
7925   for (x = 0; x < MAX_LEV_FIELDX; x++)
7926     for (y = 0; y < MAX_LEV_FIELDY; y++)
7927       if (x >= level->fieldx || y >= level->fieldy)
7928         level->field[x][y] = EL_EMPTY;
7929
7930   // copy elements to runtime playfield array
7931   for (x = 0; x < MAX_LEV_FIELDX; x++)
7932     for (y = 0; y < MAX_LEV_FIELDY; y++)
7933       Tile[x][y] = level->field[x][y];
7934
7935   // initialize level size variables for faster access
7936   lev_fieldx = level->fieldx;
7937   lev_fieldy = level->fieldy;
7938
7939   // determine border element for this level
7940   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7941     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7942   else
7943     SetBorderElement();
7944 }
7945
7946 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7947 {
7948   struct LevelFileInfo *level_file_info = &level->file_info;
7949
7950   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7951     CopyNativeLevel_RND_to_Native(level);
7952 }
7953
7954 static void LoadLevelTemplate_LoadAndInit(void)
7955 {
7956   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7957
7958   LoadLevel_InitVersion(&level_template);
7959   LoadLevel_InitElements(&level_template);
7960   LoadLevel_InitSettings(&level_template);
7961
7962   ActivateLevelTemplate();
7963 }
7964
7965 void LoadLevelTemplate(int nr)
7966 {
7967   if (!fileExists(getGlobalLevelTemplateFilename()))
7968   {
7969     Warn("no level template found for this level");
7970
7971     return;
7972   }
7973
7974   setLevelFileInfo(&level_template.file_info, nr);
7975
7976   LoadLevelTemplate_LoadAndInit();
7977 }
7978
7979 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7980 {
7981   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7982
7983   LoadLevelTemplate_LoadAndInit();
7984 }
7985
7986 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7987 {
7988   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7989
7990   if (level.use_custom_template)
7991   {
7992     if (network_level != NULL)
7993       LoadNetworkLevelTemplate(network_level);
7994     else
7995       LoadLevelTemplate(-1);
7996   }
7997
7998   LoadLevel_InitVersion(&level);
7999   LoadLevel_InitElements(&level);
8000   LoadLevel_InitPlayfield(&level);
8001   LoadLevel_InitSettings(&level);
8002
8003   LoadLevel_InitNativeEngines(&level);
8004 }
8005
8006 void LoadLevel(int nr)
8007 {
8008   SetLevelSetInfo(leveldir_current->identifier, nr);
8009
8010   setLevelFileInfo(&level.file_info, nr);
8011
8012   LoadLevel_LoadAndInit(NULL);
8013 }
8014
8015 void LoadLevelInfoOnly(int nr)
8016 {
8017   setLevelFileInfo(&level.file_info, nr);
8018
8019   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8020 }
8021
8022 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8023 {
8024   SetLevelSetInfo(network_level->leveldir_identifier,
8025                   network_level->file_info.nr);
8026
8027   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8028
8029   LoadLevel_LoadAndInit(network_level);
8030 }
8031
8032 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8033 {
8034   int chunk_size = 0;
8035
8036   chunk_size += putFileVersion(file, level->file_version);
8037   chunk_size += putFileVersion(file, level->game_version);
8038
8039   return chunk_size;
8040 }
8041
8042 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8043 {
8044   int chunk_size = 0;
8045
8046   chunk_size += putFile16BitBE(file, level->creation_date.year);
8047   chunk_size += putFile8Bit(file,    level->creation_date.month);
8048   chunk_size += putFile8Bit(file,    level->creation_date.day);
8049
8050   return chunk_size;
8051 }
8052
8053 #if ENABLE_HISTORIC_CHUNKS
8054 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8055 {
8056   int i, x, y;
8057
8058   putFile8Bit(file, level->fieldx);
8059   putFile8Bit(file, level->fieldy);
8060
8061   putFile16BitBE(file, level->time);
8062   putFile16BitBE(file, level->gems_needed);
8063
8064   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8065     putFile8Bit(file, level->name[i]);
8066
8067   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8068     putFile8Bit(file, level->score[i]);
8069
8070   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8071     for (y = 0; y < 3; y++)
8072       for (x = 0; x < 3; x++)
8073         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8074                            level->yamyam_content[i].e[x][y]));
8075   putFile8Bit(file, level->amoeba_speed);
8076   putFile8Bit(file, level->time_magic_wall);
8077   putFile8Bit(file, level->time_wheel);
8078   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8079                      level->amoeba_content));
8080   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8081   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8082   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8083   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8084
8085   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8086
8087   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8088   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8089   putFile32BitBE(file, level->can_move_into_acid_bits);
8090   putFile8Bit(file, level->dont_collide_with_bits);
8091
8092   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8093   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8094
8095   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8096   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8097   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8098
8099   putFile8Bit(file, level->game_engine_type);
8100
8101   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8102 }
8103 #endif
8104
8105 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8106 {
8107   int chunk_size = 0;
8108   int i;
8109
8110   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8111     chunk_size += putFile8Bit(file, level->name[i]);
8112
8113   return chunk_size;
8114 }
8115
8116 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8117 {
8118   int chunk_size = 0;
8119   int i;
8120
8121   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8122     chunk_size += putFile8Bit(file, level->author[i]);
8123
8124   return chunk_size;
8125 }
8126
8127 #if ENABLE_HISTORIC_CHUNKS
8128 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8129 {
8130   int chunk_size = 0;
8131   int x, y;
8132
8133   for (y = 0; y < level->fieldy; y++)
8134     for (x = 0; x < level->fieldx; x++)
8135       if (level->encoding_16bit_field)
8136         chunk_size += putFile16BitBE(file, level->field[x][y]);
8137       else
8138         chunk_size += putFile8Bit(file, level->field[x][y]);
8139
8140   return chunk_size;
8141 }
8142 #endif
8143
8144 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8145 {
8146   int chunk_size = 0;
8147   int x, y;
8148
8149   for (y = 0; y < level->fieldy; y++) 
8150     for (x = 0; x < level->fieldx; x++) 
8151       chunk_size += putFile16BitBE(file, level->field[x][y]);
8152
8153   return chunk_size;
8154 }
8155
8156 #if ENABLE_HISTORIC_CHUNKS
8157 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8158 {
8159   int i, x, y;
8160
8161   putFile8Bit(file, EL_YAMYAM);
8162   putFile8Bit(file, level->num_yamyam_contents);
8163   putFile8Bit(file, 0);
8164   putFile8Bit(file, 0);
8165
8166   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8167     for (y = 0; y < 3; y++)
8168       for (x = 0; x < 3; x++)
8169         if (level->encoding_16bit_field)
8170           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8171         else
8172           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8173 }
8174 #endif
8175
8176 #if ENABLE_HISTORIC_CHUNKS
8177 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8178 {
8179   int i, x, y;
8180   int num_contents, content_xsize, content_ysize;
8181   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8182
8183   if (element == EL_YAMYAM)
8184   {
8185     num_contents = level->num_yamyam_contents;
8186     content_xsize = 3;
8187     content_ysize = 3;
8188
8189     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8190       for (y = 0; y < 3; y++)
8191         for (x = 0; x < 3; x++)
8192           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8193   }
8194   else if (element == EL_BD_AMOEBA)
8195   {
8196     num_contents = 1;
8197     content_xsize = 1;
8198     content_ysize = 1;
8199
8200     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8201       for (y = 0; y < 3; y++)
8202         for (x = 0; x < 3; x++)
8203           content_array[i][x][y] = EL_EMPTY;
8204     content_array[0][0][0] = level->amoeba_content;
8205   }
8206   else
8207   {
8208     // chunk header already written -- write empty chunk data
8209     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8210
8211     Warn("cannot save content for element '%d'", element);
8212
8213     return;
8214   }
8215
8216   putFile16BitBE(file, element);
8217   putFile8Bit(file, num_contents);
8218   putFile8Bit(file, content_xsize);
8219   putFile8Bit(file, content_ysize);
8220
8221   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8222
8223   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8224     for (y = 0; y < 3; y++)
8225       for (x = 0; x < 3; x++)
8226         putFile16BitBE(file, content_array[i][x][y]);
8227 }
8228 #endif
8229
8230 #if ENABLE_HISTORIC_CHUNKS
8231 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8232 {
8233   int envelope_nr = element - EL_ENVELOPE_1;
8234   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8235   int chunk_size = 0;
8236   int i;
8237
8238   chunk_size += putFile16BitBE(file, element);
8239   chunk_size += putFile16BitBE(file, envelope_len);
8240   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8241   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8242
8243   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8244   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8245
8246   for (i = 0; i < envelope_len; i++)
8247     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8248
8249   return chunk_size;
8250 }
8251 #endif
8252
8253 #if ENABLE_HISTORIC_CHUNKS
8254 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8255                            int num_changed_custom_elements)
8256 {
8257   int i, check = 0;
8258
8259   putFile16BitBE(file, num_changed_custom_elements);
8260
8261   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8262   {
8263     int element = EL_CUSTOM_START + i;
8264
8265     struct ElementInfo *ei = &element_info[element];
8266
8267     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8268     {
8269       if (check < num_changed_custom_elements)
8270       {
8271         putFile16BitBE(file, element);
8272         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8273       }
8274
8275       check++;
8276     }
8277   }
8278
8279   if (check != num_changed_custom_elements)     // should not happen
8280     Warn("inconsistent number of custom element properties");
8281 }
8282 #endif
8283
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8286                            int num_changed_custom_elements)
8287 {
8288   int i, check = 0;
8289
8290   putFile16BitBE(file, num_changed_custom_elements);
8291
8292   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8293   {
8294     int element = EL_CUSTOM_START + i;
8295
8296     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8297     {
8298       if (check < num_changed_custom_elements)
8299       {
8300         putFile16BitBE(file, element);
8301         putFile16BitBE(file, element_info[element].change->target_element);
8302       }
8303
8304       check++;
8305     }
8306   }
8307
8308   if (check != num_changed_custom_elements)     // should not happen
8309     Warn("inconsistent number of custom target elements");
8310 }
8311 #endif
8312
8313 #if ENABLE_HISTORIC_CHUNKS
8314 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8315                            int num_changed_custom_elements)
8316 {
8317   int i, j, x, y, check = 0;
8318
8319   putFile16BitBE(file, num_changed_custom_elements);
8320
8321   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8322   {
8323     int element = EL_CUSTOM_START + i;
8324     struct ElementInfo *ei = &element_info[element];
8325
8326     if (ei->modified_settings)
8327     {
8328       if (check < num_changed_custom_elements)
8329       {
8330         putFile16BitBE(file, element);
8331
8332         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8333           putFile8Bit(file, ei->description[j]);
8334
8335         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8336
8337         // some free bytes for future properties and padding
8338         WriteUnusedBytesToFile(file, 7);
8339
8340         putFile8Bit(file, ei->use_gfx_element);
8341         putFile16BitBE(file, ei->gfx_element_initial);
8342
8343         putFile8Bit(file, ei->collect_score_initial);
8344         putFile8Bit(file, ei->collect_count_initial);
8345
8346         putFile16BitBE(file, ei->push_delay_fixed);
8347         putFile16BitBE(file, ei->push_delay_random);
8348         putFile16BitBE(file, ei->move_delay_fixed);
8349         putFile16BitBE(file, ei->move_delay_random);
8350
8351         putFile16BitBE(file, ei->move_pattern);
8352         putFile8Bit(file, ei->move_direction_initial);
8353         putFile8Bit(file, ei->move_stepsize);
8354
8355         for (y = 0; y < 3; y++)
8356           for (x = 0; x < 3; x++)
8357             putFile16BitBE(file, ei->content.e[x][y]);
8358
8359         putFile32BitBE(file, ei->change->events);
8360
8361         putFile16BitBE(file, ei->change->target_element);
8362
8363         putFile16BitBE(file, ei->change->delay_fixed);
8364         putFile16BitBE(file, ei->change->delay_random);
8365         putFile16BitBE(file, ei->change->delay_frames);
8366
8367         putFile16BitBE(file, ei->change->initial_trigger_element);
8368
8369         putFile8Bit(file, ei->change->explode);
8370         putFile8Bit(file, ei->change->use_target_content);
8371         putFile8Bit(file, ei->change->only_if_complete);
8372         putFile8Bit(file, ei->change->use_random_replace);
8373
8374         putFile8Bit(file, ei->change->random_percentage);
8375         putFile8Bit(file, ei->change->replace_when);
8376
8377         for (y = 0; y < 3; y++)
8378           for (x = 0; x < 3; x++)
8379             putFile16BitBE(file, ei->change->content.e[x][y]);
8380
8381         putFile8Bit(file, ei->slippery_type);
8382
8383         // some free bytes for future properties and padding
8384         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8385       }
8386
8387       check++;
8388     }
8389   }
8390
8391   if (check != num_changed_custom_elements)     // should not happen
8392     Warn("inconsistent number of custom element properties");
8393 }
8394 #endif
8395
8396 #if ENABLE_HISTORIC_CHUNKS
8397 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8398 {
8399   struct ElementInfo *ei = &element_info[element];
8400   int i, j, x, y;
8401
8402   // ---------- custom element base property values (96 bytes) ----------------
8403
8404   putFile16BitBE(file, element);
8405
8406   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8407     putFile8Bit(file, ei->description[i]);
8408
8409   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8410
8411   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8412
8413   putFile8Bit(file, ei->num_change_pages);
8414
8415   putFile16BitBE(file, ei->ce_value_fixed_initial);
8416   putFile16BitBE(file, ei->ce_value_random_initial);
8417   putFile8Bit(file, ei->use_last_ce_value);
8418
8419   putFile8Bit(file, ei->use_gfx_element);
8420   putFile16BitBE(file, ei->gfx_element_initial);
8421
8422   putFile8Bit(file, ei->collect_score_initial);
8423   putFile8Bit(file, ei->collect_count_initial);
8424
8425   putFile8Bit(file, ei->drop_delay_fixed);
8426   putFile8Bit(file, ei->push_delay_fixed);
8427   putFile8Bit(file, ei->drop_delay_random);
8428   putFile8Bit(file, ei->push_delay_random);
8429   putFile16BitBE(file, ei->move_delay_fixed);
8430   putFile16BitBE(file, ei->move_delay_random);
8431
8432   // bits 0 - 15 of "move_pattern" ...
8433   putFile16BitBE(file, ei->move_pattern & 0xffff);
8434   putFile8Bit(file, ei->move_direction_initial);
8435   putFile8Bit(file, ei->move_stepsize);
8436
8437   putFile8Bit(file, ei->slippery_type);
8438
8439   for (y = 0; y < 3; y++)
8440     for (x = 0; x < 3; x++)
8441       putFile16BitBE(file, ei->content.e[x][y]);
8442
8443   putFile16BitBE(file, ei->move_enter_element);
8444   putFile16BitBE(file, ei->move_leave_element);
8445   putFile8Bit(file, ei->move_leave_type);
8446
8447   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8448   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8449
8450   putFile8Bit(file, ei->access_direction);
8451
8452   putFile8Bit(file, ei->explosion_delay);
8453   putFile8Bit(file, ei->ignition_delay);
8454   putFile8Bit(file, ei->explosion_type);
8455
8456   // some free bytes for future custom property values and padding
8457   WriteUnusedBytesToFile(file, 1);
8458
8459   // ---------- change page property values (48 bytes) ------------------------
8460
8461   for (i = 0; i < ei->num_change_pages; i++)
8462   {
8463     struct ElementChangeInfo *change = &ei->change_page[i];
8464     unsigned int event_bits;
8465
8466     // bits 0 - 31 of "has_event[]" ...
8467     event_bits = 0;
8468     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8469       if (change->has_event[j])
8470         event_bits |= (1u << j);
8471     putFile32BitBE(file, event_bits);
8472
8473     putFile16BitBE(file, change->target_element);
8474
8475     putFile16BitBE(file, change->delay_fixed);
8476     putFile16BitBE(file, change->delay_random);
8477     putFile16BitBE(file, change->delay_frames);
8478
8479     putFile16BitBE(file, change->initial_trigger_element);
8480
8481     putFile8Bit(file, change->explode);
8482     putFile8Bit(file, change->use_target_content);
8483     putFile8Bit(file, change->only_if_complete);
8484     putFile8Bit(file, change->use_random_replace);
8485
8486     putFile8Bit(file, change->random_percentage);
8487     putFile8Bit(file, change->replace_when);
8488
8489     for (y = 0; y < 3; y++)
8490       for (x = 0; x < 3; x++)
8491         putFile16BitBE(file, change->target_content.e[x][y]);
8492
8493     putFile8Bit(file, change->can_change);
8494
8495     putFile8Bit(file, change->trigger_side);
8496
8497     putFile8Bit(file, change->trigger_player);
8498     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8499                        log_2(change->trigger_page)));
8500
8501     putFile8Bit(file, change->has_action);
8502     putFile8Bit(file, change->action_type);
8503     putFile8Bit(file, change->action_mode);
8504     putFile16BitBE(file, change->action_arg);
8505
8506     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8507     event_bits = 0;
8508     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8509       if (change->has_event[j])
8510         event_bits |= (1u << (j - 32));
8511     putFile8Bit(file, event_bits);
8512   }
8513 }
8514 #endif
8515
8516 #if ENABLE_HISTORIC_CHUNKS
8517 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8518 {
8519   struct ElementInfo *ei = &element_info[element];
8520   struct ElementGroupInfo *group = ei->group;
8521   int i;
8522
8523   putFile16BitBE(file, element);
8524
8525   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8526     putFile8Bit(file, ei->description[i]);
8527
8528   putFile8Bit(file, group->num_elements);
8529
8530   putFile8Bit(file, ei->use_gfx_element);
8531   putFile16BitBE(file, ei->gfx_element_initial);
8532
8533   putFile8Bit(file, group->choice_mode);
8534
8535   // some free bytes for future values and padding
8536   WriteUnusedBytesToFile(file, 3);
8537
8538   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8539     putFile16BitBE(file, group->element[i]);
8540 }
8541 #endif
8542
8543 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8544                                 boolean write_element)
8545 {
8546   int save_type = entry->save_type;
8547   int data_type = entry->data_type;
8548   int conf_type = entry->conf_type;
8549   int byte_mask = conf_type & CONF_MASK_BYTES;
8550   int element = entry->element;
8551   int default_value = entry->default_value;
8552   int num_bytes = 0;
8553   boolean modified = FALSE;
8554
8555   if (byte_mask != CONF_MASK_MULTI_BYTES)
8556   {
8557     void *value_ptr = entry->value;
8558     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8559                  *(int *)value_ptr);
8560
8561     // check if any settings have been modified before saving them
8562     if (value != default_value)
8563       modified = TRUE;
8564
8565     // do not save if explicitly told or if unmodified default settings
8566     if ((save_type == SAVE_CONF_NEVER) ||
8567         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8568       return 0;
8569
8570     if (write_element)
8571       num_bytes += putFile16BitBE(file, element);
8572
8573     num_bytes += putFile8Bit(file, conf_type);
8574     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8575                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8576                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8577                   0);
8578   }
8579   else if (data_type == TYPE_STRING)
8580   {
8581     char *default_string = entry->default_string;
8582     char *string = (char *)(entry->value);
8583     int string_length = strlen(string);
8584     int i;
8585
8586     // check if any settings have been modified before saving them
8587     if (!strEqual(string, default_string))
8588       modified = TRUE;
8589
8590     // do not save if explicitly told or if unmodified default settings
8591     if ((save_type == SAVE_CONF_NEVER) ||
8592         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8593       return 0;
8594
8595     if (write_element)
8596       num_bytes += putFile16BitBE(file, element);
8597
8598     num_bytes += putFile8Bit(file, conf_type);
8599     num_bytes += putFile16BitBE(file, string_length);
8600
8601     for (i = 0; i < string_length; i++)
8602       num_bytes += putFile8Bit(file, string[i]);
8603   }
8604   else if (data_type == TYPE_ELEMENT_LIST)
8605   {
8606     int *element_array = (int *)(entry->value);
8607     int num_elements = *(int *)(entry->num_entities);
8608     int i;
8609
8610     // check if any settings have been modified before saving them
8611     for (i = 0; i < num_elements; i++)
8612       if (element_array[i] != default_value)
8613         modified = TRUE;
8614
8615     // do not save if explicitly told or if unmodified default settings
8616     if ((save_type == SAVE_CONF_NEVER) ||
8617         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8618       return 0;
8619
8620     if (write_element)
8621       num_bytes += putFile16BitBE(file, element);
8622
8623     num_bytes += putFile8Bit(file, conf_type);
8624     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8625
8626     for (i = 0; i < num_elements; i++)
8627       num_bytes += putFile16BitBE(file, element_array[i]);
8628   }
8629   else if (data_type == TYPE_CONTENT_LIST)
8630   {
8631     struct Content *content = (struct Content *)(entry->value);
8632     int num_contents = *(int *)(entry->num_entities);
8633     int i, x, y;
8634
8635     // check if any settings have been modified before saving them
8636     for (i = 0; i < num_contents; i++)
8637       for (y = 0; y < 3; y++)
8638         for (x = 0; x < 3; x++)
8639           if (content[i].e[x][y] != default_value)
8640             modified = TRUE;
8641
8642     // do not save if explicitly told or if unmodified default settings
8643     if ((save_type == SAVE_CONF_NEVER) ||
8644         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8645       return 0;
8646
8647     if (write_element)
8648       num_bytes += putFile16BitBE(file, element);
8649
8650     num_bytes += putFile8Bit(file, conf_type);
8651     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8652
8653     for (i = 0; i < num_contents; i++)
8654       for (y = 0; y < 3; y++)
8655         for (x = 0; x < 3; x++)
8656           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8657   }
8658
8659   return num_bytes;
8660 }
8661
8662 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8663 {
8664   int chunk_size = 0;
8665   int i;
8666
8667   li = *level;          // copy level data into temporary buffer
8668
8669   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8670     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8671
8672   return chunk_size;
8673 }
8674
8675 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8676 {
8677   int chunk_size = 0;
8678   int i;
8679
8680   li = *level;          // copy level data into temporary buffer
8681
8682   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8683     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8684
8685   return chunk_size;
8686 }
8687
8688 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8689 {
8690   int envelope_nr = element - EL_ENVELOPE_1;
8691   int chunk_size = 0;
8692   int i;
8693
8694   chunk_size += putFile16BitBE(file, element);
8695
8696   // copy envelope data into temporary buffer
8697   xx_envelope = level->envelope[envelope_nr];
8698
8699   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8700     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8701
8702   return chunk_size;
8703 }
8704
8705 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8706 {
8707   struct ElementInfo *ei = &element_info[element];
8708   int chunk_size = 0;
8709   int i, j;
8710
8711   chunk_size += putFile16BitBE(file, element);
8712
8713   xx_ei = *ei;          // copy element data into temporary buffer
8714
8715   // set default description string for this specific element
8716   strcpy(xx_default_description, getDefaultElementDescription(ei));
8717
8718   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8719     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8720
8721   for (i = 0; i < ei->num_change_pages; i++)
8722   {
8723     struct ElementChangeInfo *change = &ei->change_page[i];
8724
8725     xx_current_change_page = i;
8726
8727     xx_change = *change;        // copy change data into temporary buffer
8728
8729     resetEventBits();
8730     setEventBitsFromEventFlags(change);
8731
8732     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8733       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8734                                          FALSE);
8735   }
8736
8737   return chunk_size;
8738 }
8739
8740 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8741 {
8742   struct ElementInfo *ei = &element_info[element];
8743   struct ElementGroupInfo *group = ei->group;
8744   int chunk_size = 0;
8745   int i;
8746
8747   chunk_size += putFile16BitBE(file, element);
8748
8749   xx_ei = *ei;          // copy element data into temporary buffer
8750   xx_group = *group;    // copy group data into temporary buffer
8751
8752   // set default description string for this specific element
8753   strcpy(xx_default_description, getDefaultElementDescription(ei));
8754
8755   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8756     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8757
8758   return chunk_size;
8759 }
8760
8761 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8762 {
8763   struct ElementInfo *ei = &element_info[element];
8764   int chunk_size = 0;
8765   int i;
8766
8767   chunk_size += putFile16BitBE(file, element);
8768
8769   xx_ei = *ei;          // copy element data into temporary buffer
8770
8771   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8772     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8773
8774   return chunk_size;
8775 }
8776
8777 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8778                                   boolean save_as_template)
8779 {
8780   int chunk_size;
8781   int i;
8782   FILE *file;
8783
8784   if (!(file = fopen(filename, MODE_WRITE)))
8785   {
8786     Warn("cannot save level file '%s'", filename);
8787
8788     return;
8789   }
8790
8791   level->file_version = FILE_VERSION_ACTUAL;
8792   level->game_version = GAME_VERSION_ACTUAL;
8793
8794   level->creation_date = getCurrentDate();
8795
8796   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8797   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8798
8799   chunk_size = SaveLevel_VERS(NULL, level);
8800   putFileChunkBE(file, "VERS", chunk_size);
8801   SaveLevel_VERS(file, level);
8802
8803   chunk_size = SaveLevel_DATE(NULL, level);
8804   putFileChunkBE(file, "DATE", chunk_size);
8805   SaveLevel_DATE(file, level);
8806
8807   chunk_size = SaveLevel_NAME(NULL, level);
8808   putFileChunkBE(file, "NAME", chunk_size);
8809   SaveLevel_NAME(file, level);
8810
8811   chunk_size = SaveLevel_AUTH(NULL, level);
8812   putFileChunkBE(file, "AUTH", chunk_size);
8813   SaveLevel_AUTH(file, level);
8814
8815   chunk_size = SaveLevel_INFO(NULL, level);
8816   putFileChunkBE(file, "INFO", chunk_size);
8817   SaveLevel_INFO(file, level);
8818
8819   chunk_size = SaveLevel_BODY(NULL, level);
8820   putFileChunkBE(file, "BODY", chunk_size);
8821   SaveLevel_BODY(file, level);
8822
8823   chunk_size = SaveLevel_ELEM(NULL, level);
8824   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8825   {
8826     putFileChunkBE(file, "ELEM", chunk_size);
8827     SaveLevel_ELEM(file, level);
8828   }
8829
8830   for (i = 0; i < NUM_ENVELOPES; i++)
8831   {
8832     int element = EL_ENVELOPE_1 + i;
8833
8834     chunk_size = SaveLevel_NOTE(NULL, level, element);
8835     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8836     {
8837       putFileChunkBE(file, "NOTE", chunk_size);
8838       SaveLevel_NOTE(file, level, element);
8839     }
8840   }
8841
8842   // if not using template level, check for non-default custom/group elements
8843   if (!level->use_custom_template || save_as_template)
8844   {
8845     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8846     {
8847       int element = EL_CUSTOM_START + i;
8848
8849       chunk_size = SaveLevel_CUSX(NULL, level, element);
8850       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8851       {
8852         putFileChunkBE(file, "CUSX", chunk_size);
8853         SaveLevel_CUSX(file, level, element);
8854       }
8855     }
8856
8857     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8858     {
8859       int element = EL_GROUP_START + i;
8860
8861       chunk_size = SaveLevel_GRPX(NULL, level, element);
8862       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8863       {
8864         putFileChunkBE(file, "GRPX", chunk_size);
8865         SaveLevel_GRPX(file, level, element);
8866       }
8867     }
8868
8869     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8870     {
8871       int element = GET_EMPTY_ELEMENT(i);
8872
8873       chunk_size = SaveLevel_EMPX(NULL, level, element);
8874       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8875       {
8876         putFileChunkBE(file, "EMPX", chunk_size);
8877         SaveLevel_EMPX(file, level, element);
8878       }
8879     }
8880   }
8881
8882   fclose(file);
8883
8884   SetFilePermissions(filename, PERMS_PRIVATE);
8885 }
8886
8887 void SaveLevel(int nr)
8888 {
8889   char *filename = getDefaultLevelFilename(nr);
8890
8891   SaveLevelFromFilename(&level, filename, FALSE);
8892 }
8893
8894 void SaveLevelTemplate(void)
8895 {
8896   char *filename = getLocalLevelTemplateFilename();
8897
8898   SaveLevelFromFilename(&level, filename, TRUE);
8899 }
8900
8901 boolean SaveLevelChecked(int nr)
8902 {
8903   char *filename = getDefaultLevelFilename(nr);
8904   boolean new_level = !fileExists(filename);
8905   boolean level_saved = FALSE;
8906
8907   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8908   {
8909     SaveLevel(nr);
8910
8911     if (new_level)
8912       Request("Level saved!", REQ_CONFIRM);
8913
8914     level_saved = TRUE;
8915   }
8916
8917   return level_saved;
8918 }
8919
8920 void DumpLevel(struct LevelInfo *level)
8921 {
8922   if (level->no_level_file || level->no_valid_file)
8923   {
8924     Warn("cannot dump -- no valid level file found");
8925
8926     return;
8927   }
8928
8929   PrintLine("-", 79);
8930   Print("Level xxx (file version %08d, game version %08d)\n",
8931         level->file_version, level->game_version);
8932   PrintLine("-", 79);
8933
8934   Print("Level author: '%s'\n", level->author);
8935   Print("Level title:  '%s'\n", level->name);
8936   Print("\n");
8937   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8938   Print("\n");
8939   Print("Level time:  %d seconds\n", level->time);
8940   Print("Gems needed: %d\n", level->gems_needed);
8941   Print("\n");
8942   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8943   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8944   Print("Time for light:      %d seconds\n", level->time_light);
8945   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8946   Print("\n");
8947   Print("Amoeba speed: %d\n", level->amoeba_speed);
8948   Print("\n");
8949
8950   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8951   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8952   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8953   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8954   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8955   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8956
8957   if (options.debug)
8958   {
8959     int i, j;
8960
8961     for (i = 0; i < NUM_ENVELOPES; i++)
8962     {
8963       char *text = level->envelope[i].text;
8964       int text_len = strlen(text);
8965       boolean has_text = FALSE;
8966
8967       for (j = 0; j < text_len; j++)
8968         if (text[j] != ' ' && text[j] != '\n')
8969           has_text = TRUE;
8970
8971       if (has_text)
8972       {
8973         Print("\n");
8974         Print("Envelope %d:\n'%s'\n", i + 1, text);
8975       }
8976     }
8977   }
8978
8979   PrintLine("-", 79);
8980 }
8981
8982 void DumpLevels(void)
8983 {
8984   static LevelDirTree *dumplevel_leveldir = NULL;
8985
8986   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8987                                                  global.dumplevel_leveldir);
8988
8989   if (dumplevel_leveldir == NULL)
8990     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8991
8992   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8993       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8994     Fail("no such level number: %d", global.dumplevel_level_nr);
8995
8996   leveldir_current = dumplevel_leveldir;
8997
8998   LoadLevel(global.dumplevel_level_nr);
8999   DumpLevel(&level);
9000
9001   CloseAllAndExit(0);
9002 }
9003
9004
9005 // ============================================================================
9006 // tape file functions
9007 // ============================================================================
9008
9009 static void setTapeInfoToDefaults(void)
9010 {
9011   int i;
9012
9013   // always start with reliable default values (empty tape)
9014   TapeErase();
9015
9016   // default values (also for pre-1.2 tapes) with only the first player
9017   tape.player_participates[0] = TRUE;
9018   for (i = 1; i < MAX_PLAYERS; i++)
9019     tape.player_participates[i] = FALSE;
9020
9021   // at least one (default: the first) player participates in every tape
9022   tape.num_participating_players = 1;
9023
9024   tape.property_bits = TAPE_PROPERTY_NONE;
9025
9026   tape.level_nr = level_nr;
9027   tape.counter = 0;
9028   tape.changed = FALSE;
9029   tape.solved = FALSE;
9030
9031   tape.recording = FALSE;
9032   tape.playing = FALSE;
9033   tape.pausing = FALSE;
9034
9035   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9036   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9037
9038   tape.no_info_chunk = TRUE;
9039   tape.no_valid_file = FALSE;
9040 }
9041
9042 static int getTapePosSize(struct TapeInfo *tape)
9043 {
9044   int tape_pos_size = 0;
9045
9046   if (tape->use_key_actions)
9047     tape_pos_size += tape->num_participating_players;
9048
9049   if (tape->use_mouse_actions)
9050     tape_pos_size += 3;         // x and y position and mouse button mask
9051
9052   tape_pos_size += 1;           // tape action delay value
9053
9054   return tape_pos_size;
9055 }
9056
9057 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9058 {
9059   tape->use_key_actions = FALSE;
9060   tape->use_mouse_actions = FALSE;
9061
9062   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9063     tape->use_key_actions = TRUE;
9064
9065   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9066     tape->use_mouse_actions = TRUE;
9067 }
9068
9069 static int getTapeActionValue(struct TapeInfo *tape)
9070 {
9071   return (tape->use_key_actions &&
9072           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9073           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9074           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9075           TAPE_ACTIONS_DEFAULT);
9076 }
9077
9078 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9079 {
9080   tape->file_version = getFileVersion(file);
9081   tape->game_version = getFileVersion(file);
9082
9083   return chunk_size;
9084 }
9085
9086 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9087 {
9088   int i;
9089
9090   tape->random_seed = getFile32BitBE(file);
9091   tape->date        = getFile32BitBE(file);
9092   tape->length      = getFile32BitBE(file);
9093
9094   // read header fields that are new since version 1.2
9095   if (tape->file_version >= FILE_VERSION_1_2)
9096   {
9097     byte store_participating_players = getFile8Bit(file);
9098     int engine_version;
9099
9100     // since version 1.2, tapes store which players participate in the tape
9101     tape->num_participating_players = 0;
9102     for (i = 0; i < MAX_PLAYERS; i++)
9103     {
9104       tape->player_participates[i] = FALSE;
9105
9106       if (store_participating_players & (1 << i))
9107       {
9108         tape->player_participates[i] = TRUE;
9109         tape->num_participating_players++;
9110       }
9111     }
9112
9113     setTapeActionFlags(tape, getFile8Bit(file));
9114
9115     tape->property_bits = getFile8Bit(file);
9116     tape->solved = getFile8Bit(file);
9117
9118     engine_version = getFileVersion(file);
9119     if (engine_version > 0)
9120       tape->engine_version = engine_version;
9121     else
9122       tape->engine_version = tape->game_version;
9123   }
9124
9125   return chunk_size;
9126 }
9127
9128 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9129 {
9130   tape->scr_fieldx = getFile8Bit(file);
9131   tape->scr_fieldy = getFile8Bit(file);
9132
9133   return chunk_size;
9134 }
9135
9136 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9137 {
9138   char *level_identifier = NULL;
9139   int level_identifier_size;
9140   int i;
9141
9142   tape->no_info_chunk = FALSE;
9143
9144   level_identifier_size = getFile16BitBE(file);
9145
9146   level_identifier = checked_malloc(level_identifier_size);
9147
9148   for (i = 0; i < level_identifier_size; i++)
9149     level_identifier[i] = getFile8Bit(file);
9150
9151   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9152   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9153
9154   checked_free(level_identifier);
9155
9156   tape->level_nr = getFile16BitBE(file);
9157
9158   chunk_size = 2 + level_identifier_size + 2;
9159
9160   return chunk_size;
9161 }
9162
9163 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9164 {
9165   int i, j;
9166   int tape_pos_size = getTapePosSize(tape);
9167   int chunk_size_expected = tape_pos_size * tape->length;
9168
9169   if (chunk_size_expected != chunk_size)
9170   {
9171     ReadUnusedBytesFromFile(file, chunk_size);
9172     return chunk_size_expected;
9173   }
9174
9175   for (i = 0; i < tape->length; i++)
9176   {
9177     if (i >= MAX_TAPE_LEN)
9178     {
9179       Warn("tape truncated -- size exceeds maximum tape size %d",
9180             MAX_TAPE_LEN);
9181
9182       // tape too large; read and ignore remaining tape data from this chunk
9183       for (;i < tape->length; i++)
9184         ReadUnusedBytesFromFile(file, tape_pos_size);
9185
9186       break;
9187     }
9188
9189     if (tape->use_key_actions)
9190     {
9191       for (j = 0; j < MAX_PLAYERS; j++)
9192       {
9193         tape->pos[i].action[j] = MV_NONE;
9194
9195         if (tape->player_participates[j])
9196           tape->pos[i].action[j] = getFile8Bit(file);
9197       }
9198     }
9199
9200     if (tape->use_mouse_actions)
9201     {
9202       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9203       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9204       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9205     }
9206
9207     tape->pos[i].delay = getFile8Bit(file);
9208
9209     if (tape->file_version == FILE_VERSION_1_0)
9210     {
9211       // eliminate possible diagonal moves in old tapes
9212       // this is only for backward compatibility
9213
9214       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9215       byte action = tape->pos[i].action[0];
9216       int k, num_moves = 0;
9217
9218       for (k = 0; k < 4; k++)
9219       {
9220         if (action & joy_dir[k])
9221         {
9222           tape->pos[i + num_moves].action[0] = joy_dir[k];
9223           if (num_moves > 0)
9224             tape->pos[i + num_moves].delay = 0;
9225           num_moves++;
9226         }
9227       }
9228
9229       if (num_moves > 1)
9230       {
9231         num_moves--;
9232         i += num_moves;
9233         tape->length += num_moves;
9234       }
9235     }
9236     else if (tape->file_version < FILE_VERSION_2_0)
9237     {
9238       // convert pre-2.0 tapes to new tape format
9239
9240       if (tape->pos[i].delay > 1)
9241       {
9242         // action part
9243         tape->pos[i + 1] = tape->pos[i];
9244         tape->pos[i + 1].delay = 1;
9245
9246         // delay part
9247         for (j = 0; j < MAX_PLAYERS; j++)
9248           tape->pos[i].action[j] = MV_NONE;
9249         tape->pos[i].delay--;
9250
9251         i++;
9252         tape->length++;
9253       }
9254     }
9255
9256     if (checkEndOfFile(file))
9257       break;
9258   }
9259
9260   if (i != tape->length)
9261     chunk_size = tape_pos_size * i;
9262
9263   return chunk_size;
9264 }
9265
9266 static void LoadTape_SokobanSolution(char *filename)
9267 {
9268   File *file;
9269   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9270
9271   if (!(file = openFile(filename, MODE_READ)))
9272   {
9273     tape.no_valid_file = TRUE;
9274
9275     return;
9276   }
9277
9278   while (!checkEndOfFile(file))
9279   {
9280     unsigned char c = getByteFromFile(file);
9281
9282     if (checkEndOfFile(file))
9283       break;
9284
9285     switch (c)
9286     {
9287       case 'u':
9288       case 'U':
9289         tape.pos[tape.length].action[0] = MV_UP;
9290         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9291         tape.length++;
9292         break;
9293
9294       case 'd':
9295       case 'D':
9296         tape.pos[tape.length].action[0] = MV_DOWN;
9297         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9298         tape.length++;
9299         break;
9300
9301       case 'l':
9302       case 'L':
9303         tape.pos[tape.length].action[0] = MV_LEFT;
9304         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9305         tape.length++;
9306         break;
9307
9308       case 'r':
9309       case 'R':
9310         tape.pos[tape.length].action[0] = MV_RIGHT;
9311         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9312         tape.length++;
9313         break;
9314
9315       case '\n':
9316       case '\r':
9317       case '\t':
9318       case ' ':
9319         // ignore white-space characters
9320         break;
9321
9322       default:
9323         tape.no_valid_file = TRUE;
9324
9325         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9326
9327         break;
9328     }
9329   }
9330
9331   closeFile(file);
9332
9333   if (tape.no_valid_file)
9334     return;
9335
9336   tape.length_frames  = GetTapeLengthFrames();
9337   tape.length_seconds = GetTapeLengthSeconds();
9338 }
9339
9340 void LoadTapeFromFilename(char *filename)
9341 {
9342   char cookie[MAX_LINE_LEN];
9343   char chunk_name[CHUNK_ID_LEN + 1];
9344   File *file;
9345   int chunk_size;
9346
9347   // always start with reliable default values
9348   setTapeInfoToDefaults();
9349
9350   if (strSuffix(filename, ".sln"))
9351   {
9352     LoadTape_SokobanSolution(filename);
9353
9354     return;
9355   }
9356
9357   if (!(file = openFile(filename, MODE_READ)))
9358   {
9359     tape.no_valid_file = TRUE;
9360
9361     return;
9362   }
9363
9364   getFileChunkBE(file, chunk_name, NULL);
9365   if (strEqual(chunk_name, "RND1"))
9366   {
9367     getFile32BitBE(file);               // not used
9368
9369     getFileChunkBE(file, chunk_name, NULL);
9370     if (!strEqual(chunk_name, "TAPE"))
9371     {
9372       tape.no_valid_file = TRUE;
9373
9374       Warn("unknown format of tape file '%s'", filename);
9375
9376       closeFile(file);
9377
9378       return;
9379     }
9380   }
9381   else  // check for pre-2.0 file format with cookie string
9382   {
9383     strcpy(cookie, chunk_name);
9384     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9385       cookie[4] = '\0';
9386     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9387       cookie[strlen(cookie) - 1] = '\0';
9388
9389     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9390     {
9391       tape.no_valid_file = TRUE;
9392
9393       Warn("unknown format of tape file '%s'", filename);
9394
9395       closeFile(file);
9396
9397       return;
9398     }
9399
9400     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9401     {
9402       tape.no_valid_file = TRUE;
9403
9404       Warn("unsupported version of tape file '%s'", filename);
9405
9406       closeFile(file);
9407
9408       return;
9409     }
9410
9411     // pre-2.0 tape files have no game version, so use file version here
9412     tape.game_version = tape.file_version;
9413   }
9414
9415   if (tape.file_version < FILE_VERSION_1_2)
9416   {
9417     // tape files from versions before 1.2.0 without chunk structure
9418     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9419     LoadTape_BODY(file, 2 * tape.length,      &tape);
9420   }
9421   else
9422   {
9423     static struct
9424     {
9425       char *name;
9426       int size;
9427       int (*loader)(File *, int, struct TapeInfo *);
9428     }
9429     chunk_info[] =
9430     {
9431       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9432       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9433       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9434       { "INFO", -1,                     LoadTape_INFO },
9435       { "BODY", -1,                     LoadTape_BODY },
9436       {  NULL,  0,                      NULL }
9437     };
9438
9439     while (getFileChunkBE(file, chunk_name, &chunk_size))
9440     {
9441       int i = 0;
9442
9443       while (chunk_info[i].name != NULL &&
9444              !strEqual(chunk_name, chunk_info[i].name))
9445         i++;
9446
9447       if (chunk_info[i].name == NULL)
9448       {
9449         Warn("unknown chunk '%s' in tape file '%s'",
9450               chunk_name, filename);
9451
9452         ReadUnusedBytesFromFile(file, chunk_size);
9453       }
9454       else if (chunk_info[i].size != -1 &&
9455                chunk_info[i].size != chunk_size)
9456       {
9457         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9458               chunk_size, chunk_name, filename);
9459
9460         ReadUnusedBytesFromFile(file, chunk_size);
9461       }
9462       else
9463       {
9464         // call function to load this tape chunk
9465         int chunk_size_expected =
9466           (chunk_info[i].loader)(file, chunk_size, &tape);
9467
9468         // the size of some chunks cannot be checked before reading other
9469         // chunks first (like "HEAD" and "BODY") that contain some header
9470         // information, so check them here
9471         if (chunk_size_expected != chunk_size)
9472         {
9473           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9474                 chunk_size, chunk_name, filename);
9475         }
9476       }
9477     }
9478   }
9479
9480   closeFile(file);
9481
9482   tape.length_frames  = GetTapeLengthFrames();
9483   tape.length_seconds = GetTapeLengthSeconds();
9484
9485 #if 0
9486   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9487         tape.file_version);
9488   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9489         tape.game_version);
9490   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9491         tape.engine_version);
9492 #endif
9493 }
9494
9495 void LoadTape(int nr)
9496 {
9497   char *filename = getTapeFilename(nr);
9498
9499   LoadTapeFromFilename(filename);
9500 }
9501
9502 void LoadSolutionTape(int nr)
9503 {
9504   char *filename = getSolutionTapeFilename(nr);
9505
9506   LoadTapeFromFilename(filename);
9507
9508   if (TAPE_IS_EMPTY(tape))
9509   {
9510     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9511         level.native_bd_level->replay != NULL)
9512       CopyNativeTape_BD_to_RND(&level);
9513     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9514         level.native_sp_level->demo.is_available)
9515       CopyNativeTape_SP_to_RND(&level);
9516   }
9517 }
9518
9519 void LoadScoreTape(char *score_tape_basename, int nr)
9520 {
9521   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9522
9523   LoadTapeFromFilename(filename);
9524 }
9525
9526 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9527 {
9528   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9529
9530   LoadTapeFromFilename(filename);
9531 }
9532
9533 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9534 {
9535   // chunk required for team mode tapes with non-default screen size
9536   return (tape->num_participating_players > 1 &&
9537           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9538            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9539 }
9540
9541 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9542 {
9543   putFileVersion(file, tape->file_version);
9544   putFileVersion(file, tape->game_version);
9545 }
9546
9547 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9548 {
9549   int i;
9550   byte store_participating_players = 0;
9551
9552   // set bits for participating players for compact storage
9553   for (i = 0; i < MAX_PLAYERS; i++)
9554     if (tape->player_participates[i])
9555       store_participating_players |= (1 << i);
9556
9557   putFile32BitBE(file, tape->random_seed);
9558   putFile32BitBE(file, tape->date);
9559   putFile32BitBE(file, tape->length);
9560
9561   putFile8Bit(file, store_participating_players);
9562
9563   putFile8Bit(file, getTapeActionValue(tape));
9564
9565   putFile8Bit(file, tape->property_bits);
9566   putFile8Bit(file, tape->solved);
9567
9568   putFileVersion(file, tape->engine_version);
9569 }
9570
9571 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9572 {
9573   putFile8Bit(file, tape->scr_fieldx);
9574   putFile8Bit(file, tape->scr_fieldy);
9575 }
9576
9577 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9578 {
9579   int level_identifier_size = strlen(tape->level_identifier) + 1;
9580   int i;
9581
9582   putFile16BitBE(file, level_identifier_size);
9583
9584   for (i = 0; i < level_identifier_size; i++)
9585     putFile8Bit(file, tape->level_identifier[i]);
9586
9587   putFile16BitBE(file, tape->level_nr);
9588 }
9589
9590 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9591 {
9592   int i, j;
9593
9594   for (i = 0; i < tape->length; i++)
9595   {
9596     if (tape->use_key_actions)
9597     {
9598       for (j = 0; j < MAX_PLAYERS; j++)
9599         if (tape->player_participates[j])
9600           putFile8Bit(file, tape->pos[i].action[j]);
9601     }
9602
9603     if (tape->use_mouse_actions)
9604     {
9605       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9606       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9607       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9608     }
9609
9610     putFile8Bit(file, tape->pos[i].delay);
9611   }
9612 }
9613
9614 void SaveTapeToFilename(char *filename)
9615 {
9616   FILE *file;
9617   int tape_pos_size;
9618   int info_chunk_size;
9619   int body_chunk_size;
9620
9621   if (!(file = fopen(filename, MODE_WRITE)))
9622   {
9623     Warn("cannot save level recording file '%s'", filename);
9624
9625     return;
9626   }
9627
9628   tape_pos_size = getTapePosSize(&tape);
9629
9630   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9631   body_chunk_size = tape_pos_size * tape.length;
9632
9633   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9634   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9635
9636   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9637   SaveTape_VERS(file, &tape);
9638
9639   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9640   SaveTape_HEAD(file, &tape);
9641
9642   if (checkSaveTape_SCRN(&tape))
9643   {
9644     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9645     SaveTape_SCRN(file, &tape);
9646   }
9647
9648   putFileChunkBE(file, "INFO", info_chunk_size);
9649   SaveTape_INFO(file, &tape);
9650
9651   putFileChunkBE(file, "BODY", body_chunk_size);
9652   SaveTape_BODY(file, &tape);
9653
9654   fclose(file);
9655
9656   SetFilePermissions(filename, PERMS_PRIVATE);
9657 }
9658
9659 static void SaveTapeExt(char *filename)
9660 {
9661   int i;
9662
9663   tape.file_version = FILE_VERSION_ACTUAL;
9664   tape.game_version = GAME_VERSION_ACTUAL;
9665
9666   tape.num_participating_players = 0;
9667
9668   // count number of participating players
9669   for (i = 0; i < MAX_PLAYERS; i++)
9670     if (tape.player_participates[i])
9671       tape.num_participating_players++;
9672
9673   SaveTapeToFilename(filename);
9674
9675   tape.changed = FALSE;
9676 }
9677
9678 void SaveTape(int nr)
9679 {
9680   char *filename = getTapeFilename(nr);
9681
9682   InitTapeDirectory(leveldir_current->subdir);
9683
9684   SaveTapeExt(filename);
9685 }
9686
9687 void SaveScoreTape(int nr)
9688 {
9689   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9690
9691   // used instead of "leveldir_current->subdir" (for network games)
9692   InitScoreTapeDirectory(levelset.identifier, nr);
9693
9694   SaveTapeExt(filename);
9695 }
9696
9697 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9698                                   unsigned int req_state_added)
9699 {
9700   char *filename = getTapeFilename(nr);
9701   boolean new_tape = !fileExists(filename);
9702   boolean tape_saved = FALSE;
9703
9704   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9705   {
9706     SaveTape(nr);
9707
9708     if (new_tape)
9709       Request(msg_saved, REQ_CONFIRM | req_state_added);
9710
9711     tape_saved = TRUE;
9712   }
9713
9714   return tape_saved;
9715 }
9716
9717 boolean SaveTapeChecked(int nr)
9718 {
9719   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9720 }
9721
9722 boolean SaveTapeChecked_LevelSolved(int nr)
9723 {
9724   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9725                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9726 }
9727
9728 void DumpTape(struct TapeInfo *tape)
9729 {
9730   int tape_frame_counter;
9731   int i, j;
9732
9733   if (tape->no_valid_file)
9734   {
9735     Warn("cannot dump -- no valid tape file found");
9736
9737     return;
9738   }
9739
9740   PrintLine("-", 79);
9741
9742   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9743         tape->level_nr, tape->file_version, tape->game_version);
9744   Print("                  (effective engine version %08d)\n",
9745         tape->engine_version);
9746   Print("Level series identifier: '%s'\n", tape->level_identifier);
9747
9748   Print("Solution tape: %s\n",
9749         tape->solved ? "yes" :
9750         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9751
9752   Print("Special tape properties: ");
9753   if (tape->property_bits == TAPE_PROPERTY_NONE)
9754     Print("[none]");
9755   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9756     Print("[em_random_bug]");
9757   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9758     Print("[game_speed]");
9759   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9760     Print("[pause]");
9761   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9762     Print("[single_step]");
9763   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9764     Print("[snapshot]");
9765   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9766     Print("[replayed]");
9767   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9768     Print("[tas_keys]");
9769   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9770     Print("[small_graphics]");
9771   Print("\n");
9772
9773   int year2 = tape->date / 10000;
9774   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9775   int month_index_raw = (tape->date / 100) % 100;
9776   int month_index = month_index_raw % 12;       // prevent invalid index
9777   int month = month_index + 1;
9778   int day = tape->date % 100;
9779
9780   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9781
9782   PrintLine("-", 79);
9783
9784   tape_frame_counter = 0;
9785
9786   for (i = 0; i < tape->length; i++)
9787   {
9788     if (i >= MAX_TAPE_LEN)
9789       break;
9790
9791     Print("%04d: ", i);
9792
9793     for (j = 0; j < MAX_PLAYERS; j++)
9794     {
9795       if (tape->player_participates[j])
9796       {
9797         int action = tape->pos[i].action[j];
9798
9799         Print("%d:%02x ", j, action);
9800         Print("[%c%c%c%c|%c%c] - ",
9801               (action & JOY_LEFT ? '<' : ' '),
9802               (action & JOY_RIGHT ? '>' : ' '),
9803               (action & JOY_UP ? '^' : ' '),
9804               (action & JOY_DOWN ? 'v' : ' '),
9805               (action & JOY_BUTTON_1 ? '1' : ' '),
9806               (action & JOY_BUTTON_2 ? '2' : ' '));
9807       }
9808     }
9809
9810     Print("(%03d) ", tape->pos[i].delay);
9811     Print("[%05d]\n", tape_frame_counter);
9812
9813     tape_frame_counter += tape->pos[i].delay;
9814   }
9815
9816   PrintLine("-", 79);
9817 }
9818
9819 void DumpTapes(void)
9820 {
9821   static LevelDirTree *dumptape_leveldir = NULL;
9822
9823   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9824                                                 global.dumptape_leveldir);
9825
9826   if (dumptape_leveldir == NULL)
9827     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9828
9829   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9830       global.dumptape_level_nr > dumptape_leveldir->last_level)
9831     Fail("no such level number: %d", global.dumptape_level_nr);
9832
9833   leveldir_current = dumptape_leveldir;
9834
9835   if (options.mytapes)
9836     LoadTape(global.dumptape_level_nr);
9837   else
9838     LoadSolutionTape(global.dumptape_level_nr);
9839
9840   DumpTape(&tape);
9841
9842   CloseAllAndExit(0);
9843 }
9844
9845
9846 // ============================================================================
9847 // score file functions
9848 // ============================================================================
9849
9850 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9851 {
9852   int i;
9853
9854   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9855   {
9856     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9857     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9858     scores->entry[i].score = 0;
9859     scores->entry[i].time = 0;
9860
9861     scores->entry[i].id = -1;
9862     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9863     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9864     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9865     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9866     strcpy(scores->entry[i].country_code, "??");
9867   }
9868
9869   scores->num_entries = 0;
9870   scores->last_added = -1;
9871   scores->last_added_local = -1;
9872
9873   scores->updated = FALSE;
9874   scores->uploaded = FALSE;
9875   scores->tape_downloaded = FALSE;
9876   scores->force_last_added = FALSE;
9877
9878   // The following values are intentionally not reset here:
9879   // - last_level_nr
9880   // - last_entry_nr
9881   // - next_level_nr
9882   // - continue_playing
9883   // - continue_on_return
9884 }
9885
9886 static void setScoreInfoToDefaults(void)
9887 {
9888   setScoreInfoToDefaultsExt(&scores);
9889 }
9890
9891 static void setServerScoreInfoToDefaults(void)
9892 {
9893   setScoreInfoToDefaultsExt(&server_scores);
9894 }
9895
9896 static void LoadScore_OLD(int nr)
9897 {
9898   int i;
9899   char *filename = getScoreFilename(nr);
9900   char cookie[MAX_LINE_LEN];
9901   char line[MAX_LINE_LEN];
9902   char *line_ptr;
9903   FILE *file;
9904
9905   if (!(file = fopen(filename, MODE_READ)))
9906     return;
9907
9908   // check file identifier
9909   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9910     cookie[0] = '\0';
9911   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9912     cookie[strlen(cookie) - 1] = '\0';
9913
9914   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9915   {
9916     Warn("unknown format of score file '%s'", filename);
9917
9918     fclose(file);
9919
9920     return;
9921   }
9922
9923   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9924   {
9925     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9926       Warn("fscanf() failed; %s", strerror(errno));
9927
9928     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9929       line[0] = '\0';
9930
9931     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9932       line[strlen(line) - 1] = '\0';
9933
9934     for (line_ptr = line; *line_ptr; line_ptr++)
9935     {
9936       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9937       {
9938         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9939         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9940         break;
9941       }
9942     }
9943   }
9944
9945   fclose(file);
9946 }
9947
9948 static void ConvertScore_OLD(void)
9949 {
9950   // only convert score to time for levels that rate playing time over score
9951   if (!level.rate_time_over_score)
9952     return;
9953
9954   // convert old score to playing time for score-less levels (like Supaplex)
9955   int time_final_max = 999;
9956   int i;
9957
9958   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9959   {
9960     int score = scores.entry[i].score;
9961
9962     if (score > 0 && score < time_final_max)
9963       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9964   }
9965 }
9966
9967 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9968 {
9969   scores->file_version = getFileVersion(file);
9970   scores->game_version = getFileVersion(file);
9971
9972   return chunk_size;
9973 }
9974
9975 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9976 {
9977   char *level_identifier = NULL;
9978   int level_identifier_size;
9979   int i;
9980
9981   level_identifier_size = getFile16BitBE(file);
9982
9983   level_identifier = checked_malloc(level_identifier_size);
9984
9985   for (i = 0; i < level_identifier_size; i++)
9986     level_identifier[i] = getFile8Bit(file);
9987
9988   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9989   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9990
9991   checked_free(level_identifier);
9992
9993   scores->level_nr = getFile16BitBE(file);
9994   scores->num_entries = getFile16BitBE(file);
9995
9996   chunk_size = 2 + level_identifier_size + 2 + 2;
9997
9998   return chunk_size;
9999 }
10000
10001 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10002 {
10003   int i, j;
10004
10005   for (i = 0; i < scores->num_entries; i++)
10006   {
10007     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10008       scores->entry[i].name[j] = getFile8Bit(file);
10009
10010     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10011   }
10012
10013   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10014
10015   return chunk_size;
10016 }
10017
10018 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10019 {
10020   int i;
10021
10022   for (i = 0; i < scores->num_entries; i++)
10023     scores->entry[i].score = getFile16BitBE(file);
10024
10025   chunk_size = scores->num_entries * 2;
10026
10027   return chunk_size;
10028 }
10029
10030 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10031 {
10032   int i;
10033
10034   for (i = 0; i < scores->num_entries; i++)
10035     scores->entry[i].score = getFile32BitBE(file);
10036
10037   chunk_size = scores->num_entries * 4;
10038
10039   return chunk_size;
10040 }
10041
10042 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10043 {
10044   int i;
10045
10046   for (i = 0; i < scores->num_entries; i++)
10047     scores->entry[i].time = getFile32BitBE(file);
10048
10049   chunk_size = scores->num_entries * 4;
10050
10051   return chunk_size;
10052 }
10053
10054 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10055 {
10056   int i, j;
10057
10058   for (i = 0; i < scores->num_entries; i++)
10059   {
10060     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10061       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10062
10063     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10064   }
10065
10066   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10067
10068   return chunk_size;
10069 }
10070
10071 void LoadScore(int nr)
10072 {
10073   char *filename = getScoreFilename(nr);
10074   char cookie[MAX_LINE_LEN];
10075   char chunk_name[CHUNK_ID_LEN + 1];
10076   int chunk_size;
10077   boolean old_score_file_format = FALSE;
10078   File *file;
10079
10080   // always start with reliable default values
10081   setScoreInfoToDefaults();
10082
10083   if (!(file = openFile(filename, MODE_READ)))
10084     return;
10085
10086   getFileChunkBE(file, chunk_name, NULL);
10087   if (strEqual(chunk_name, "RND1"))
10088   {
10089     getFile32BitBE(file);               // not used
10090
10091     getFileChunkBE(file, chunk_name, NULL);
10092     if (!strEqual(chunk_name, "SCOR"))
10093     {
10094       Warn("unknown format of score file '%s'", filename);
10095
10096       closeFile(file);
10097
10098       return;
10099     }
10100   }
10101   else  // check for old file format with cookie string
10102   {
10103     strcpy(cookie, chunk_name);
10104     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10105       cookie[4] = '\0';
10106     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10107       cookie[strlen(cookie) - 1] = '\0';
10108
10109     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10110     {
10111       Warn("unknown format of score file '%s'", filename);
10112
10113       closeFile(file);
10114
10115       return;
10116     }
10117
10118     old_score_file_format = TRUE;
10119   }
10120
10121   if (old_score_file_format)
10122   {
10123     // score files from versions before 4.2.4.0 without chunk structure
10124     LoadScore_OLD(nr);
10125
10126     // convert score to time, if possible (mainly for Supaplex levels)
10127     ConvertScore_OLD();
10128   }
10129   else
10130   {
10131     static struct
10132     {
10133       char *name;
10134       int size;
10135       int (*loader)(File *, int, struct ScoreInfo *);
10136     }
10137     chunk_info[] =
10138     {
10139       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10140       { "INFO", -1,                     LoadScore_INFO },
10141       { "NAME", -1,                     LoadScore_NAME },
10142       { "SCOR", -1,                     LoadScore_SCOR },
10143       { "SC4R", -1,                     LoadScore_SC4R },
10144       { "TIME", -1,                     LoadScore_TIME },
10145       { "TAPE", -1,                     LoadScore_TAPE },
10146
10147       {  NULL,  0,                      NULL }
10148     };
10149
10150     while (getFileChunkBE(file, chunk_name, &chunk_size))
10151     {
10152       int i = 0;
10153
10154       while (chunk_info[i].name != NULL &&
10155              !strEqual(chunk_name, chunk_info[i].name))
10156         i++;
10157
10158       if (chunk_info[i].name == NULL)
10159       {
10160         Warn("unknown chunk '%s' in score file '%s'",
10161               chunk_name, filename);
10162
10163         ReadUnusedBytesFromFile(file, chunk_size);
10164       }
10165       else if (chunk_info[i].size != -1 &&
10166                chunk_info[i].size != chunk_size)
10167       {
10168         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10169               chunk_size, chunk_name, filename);
10170
10171         ReadUnusedBytesFromFile(file, chunk_size);
10172       }
10173       else
10174       {
10175         // call function to load this score chunk
10176         int chunk_size_expected =
10177           (chunk_info[i].loader)(file, chunk_size, &scores);
10178
10179         // the size of some chunks cannot be checked before reading other
10180         // chunks first (like "HEAD" and "BODY") that contain some header
10181         // information, so check them here
10182         if (chunk_size_expected != chunk_size)
10183         {
10184           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10185                 chunk_size, chunk_name, filename);
10186         }
10187       }
10188     }
10189   }
10190
10191   closeFile(file);
10192 }
10193
10194 #if ENABLE_HISTORIC_CHUNKS
10195 void SaveScore_OLD(int nr)
10196 {
10197   int i;
10198   char *filename = getScoreFilename(nr);
10199   FILE *file;
10200
10201   // used instead of "leveldir_current->subdir" (for network games)
10202   InitScoreDirectory(levelset.identifier);
10203
10204   if (!(file = fopen(filename, MODE_WRITE)))
10205   {
10206     Warn("cannot save score for level %d", nr);
10207
10208     return;
10209   }
10210
10211   fprintf(file, "%s\n\n", SCORE_COOKIE);
10212
10213   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10214     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10215
10216   fclose(file);
10217
10218   SetFilePermissions(filename, PERMS_PRIVATE);
10219 }
10220 #endif
10221
10222 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10223 {
10224   putFileVersion(file, scores->file_version);
10225   putFileVersion(file, scores->game_version);
10226 }
10227
10228 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10229 {
10230   int level_identifier_size = strlen(scores->level_identifier) + 1;
10231   int i;
10232
10233   putFile16BitBE(file, level_identifier_size);
10234
10235   for (i = 0; i < level_identifier_size; i++)
10236     putFile8Bit(file, scores->level_identifier[i]);
10237
10238   putFile16BitBE(file, scores->level_nr);
10239   putFile16BitBE(file, scores->num_entries);
10240 }
10241
10242 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10243 {
10244   int i, j;
10245
10246   for (i = 0; i < scores->num_entries; i++)
10247   {
10248     int name_size = strlen(scores->entry[i].name);
10249
10250     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10251       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10252   }
10253 }
10254
10255 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10256 {
10257   int i;
10258
10259   for (i = 0; i < scores->num_entries; i++)
10260     putFile16BitBE(file, scores->entry[i].score);
10261 }
10262
10263 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10264 {
10265   int i;
10266
10267   for (i = 0; i < scores->num_entries; i++)
10268     putFile32BitBE(file, scores->entry[i].score);
10269 }
10270
10271 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10272 {
10273   int i;
10274
10275   for (i = 0; i < scores->num_entries; i++)
10276     putFile32BitBE(file, scores->entry[i].time);
10277 }
10278
10279 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10280 {
10281   int i, j;
10282
10283   for (i = 0; i < scores->num_entries; i++)
10284   {
10285     int size = strlen(scores->entry[i].tape_basename);
10286
10287     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10288       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10289   }
10290 }
10291
10292 static void SaveScoreToFilename(char *filename)
10293 {
10294   FILE *file;
10295   int info_chunk_size;
10296   int name_chunk_size;
10297   int scor_chunk_size;
10298   int sc4r_chunk_size;
10299   int time_chunk_size;
10300   int tape_chunk_size;
10301   boolean has_large_score_values;
10302   int i;
10303
10304   if (!(file = fopen(filename, MODE_WRITE)))
10305   {
10306     Warn("cannot save score file '%s'", filename);
10307
10308     return;
10309   }
10310
10311   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10312   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10313   scor_chunk_size = scores.num_entries * 2;
10314   sc4r_chunk_size = scores.num_entries * 4;
10315   time_chunk_size = scores.num_entries * 4;
10316   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10317
10318   has_large_score_values = FALSE;
10319   for (i = 0; i < scores.num_entries; i++)
10320     if (scores.entry[i].score > 0xffff)
10321       has_large_score_values = TRUE;
10322
10323   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10324   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10325
10326   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10327   SaveScore_VERS(file, &scores);
10328
10329   putFileChunkBE(file, "INFO", info_chunk_size);
10330   SaveScore_INFO(file, &scores);
10331
10332   putFileChunkBE(file, "NAME", name_chunk_size);
10333   SaveScore_NAME(file, &scores);
10334
10335   if (has_large_score_values)
10336   {
10337     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10338     SaveScore_SC4R(file, &scores);
10339   }
10340   else
10341   {
10342     putFileChunkBE(file, "SCOR", scor_chunk_size);
10343     SaveScore_SCOR(file, &scores);
10344   }
10345
10346   putFileChunkBE(file, "TIME", time_chunk_size);
10347   SaveScore_TIME(file, &scores);
10348
10349   putFileChunkBE(file, "TAPE", tape_chunk_size);
10350   SaveScore_TAPE(file, &scores);
10351
10352   fclose(file);
10353
10354   SetFilePermissions(filename, PERMS_PRIVATE);
10355 }
10356
10357 void SaveScore(int nr)
10358 {
10359   char *filename = getScoreFilename(nr);
10360   int i;
10361
10362   // used instead of "leveldir_current->subdir" (for network games)
10363   InitScoreDirectory(levelset.identifier);
10364
10365   scores.file_version = FILE_VERSION_ACTUAL;
10366   scores.game_version = GAME_VERSION_ACTUAL;
10367
10368   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10369   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10370   scores.level_nr = level_nr;
10371
10372   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10373     if (scores.entry[i].score == 0 &&
10374         scores.entry[i].time == 0 &&
10375         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10376       break;
10377
10378   scores.num_entries = i;
10379
10380   if (scores.num_entries == 0)
10381     return;
10382
10383   SaveScoreToFilename(filename);
10384 }
10385
10386 static void LoadServerScoreFromCache(int nr)
10387 {
10388   struct ScoreEntry score_entry;
10389   struct
10390   {
10391     void *value;
10392     boolean is_string;
10393     int string_size;
10394   }
10395   score_mapping[] =
10396   {
10397     { &score_entry.score,               FALSE,  0                       },
10398     { &score_entry.time,                FALSE,  0                       },
10399     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10400     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10401     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10402     { &score_entry.id,                  FALSE,  0                       },
10403     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10404     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10405     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10406     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10407
10408     { NULL,                             FALSE,  0                       }
10409   };
10410   char *filename = getScoreCacheFilename(nr);
10411   SetupFileHash *score_hash = loadSetupFileHash(filename);
10412   int i, j;
10413
10414   server_scores.num_entries = 0;
10415
10416   if (score_hash == NULL)
10417     return;
10418
10419   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10420   {
10421     score_entry = server_scores.entry[i];
10422
10423     for (j = 0; score_mapping[j].value != NULL; j++)
10424     {
10425       char token[10];
10426
10427       sprintf(token, "%02d.%d", i, j);
10428
10429       char *value = getHashEntry(score_hash, token);
10430
10431       if (value == NULL)
10432         continue;
10433
10434       if (score_mapping[j].is_string)
10435       {
10436         char *score_value = (char *)score_mapping[j].value;
10437         int value_size = score_mapping[j].string_size;
10438
10439         strncpy(score_value, value, value_size);
10440         score_value[value_size] = '\0';
10441       }
10442       else
10443       {
10444         int *score_value = (int *)score_mapping[j].value;
10445
10446         *score_value = atoi(value);
10447       }
10448
10449       server_scores.num_entries = i + 1;
10450     }
10451
10452     server_scores.entry[i] = score_entry;
10453   }
10454
10455   freeSetupFileHash(score_hash);
10456 }
10457
10458 void LoadServerScore(int nr, boolean download_score)
10459 {
10460   if (!setup.use_api_server)
10461     return;
10462
10463   // always start with reliable default values
10464   setServerScoreInfoToDefaults();
10465
10466   // 1st step: load server scores from cache file (which may not exist)
10467   // (this should prevent reading it while the thread is writing to it)
10468   LoadServerScoreFromCache(nr);
10469
10470   if (download_score && runtime.use_api_server)
10471   {
10472     // 2nd step: download server scores from score server to cache file
10473     // (as thread, as it might time out if the server is not reachable)
10474     ApiGetScoreAsThread(nr);
10475   }
10476 }
10477
10478 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10479 {
10480   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10481
10482   // if score tape not uploaded, ask for uploading missing tapes later
10483   if (!setup.has_remaining_tapes)
10484     setup.ask_for_remaining_tapes = TRUE;
10485
10486   setup.provide_uploading_tapes = TRUE;
10487   setup.has_remaining_tapes = TRUE;
10488
10489   SaveSetup_ServerSetup();
10490 }
10491
10492 void SaveServerScore(int nr, boolean tape_saved)
10493 {
10494   if (!runtime.use_api_server)
10495   {
10496     PrepareScoreTapesForUpload(leveldir_current->subdir);
10497
10498     return;
10499   }
10500
10501   ApiAddScoreAsThread(nr, tape_saved, NULL);
10502 }
10503
10504 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10505                              char *score_tape_filename)
10506 {
10507   if (!runtime.use_api_server)
10508     return;
10509
10510   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10511 }
10512
10513 void LoadLocalAndServerScore(int nr, boolean download_score)
10514 {
10515   int last_added_local = scores.last_added_local;
10516   boolean force_last_added = scores.force_last_added;
10517
10518   // needed if only showing server scores
10519   setScoreInfoToDefaults();
10520
10521   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10522     LoadScore(nr);
10523
10524   // restore last added local score entry (before merging server scores)
10525   scores.last_added = scores.last_added_local = last_added_local;
10526
10527   if (setup.use_api_server &&
10528       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10529   {
10530     // load server scores from cache file and trigger update from server
10531     LoadServerScore(nr, download_score);
10532
10533     // merge local scores with scores from server
10534     MergeServerScore();
10535   }
10536
10537   if (force_last_added)
10538     scores.force_last_added = force_last_added;
10539 }
10540
10541
10542 // ============================================================================
10543 // setup file functions
10544 // ============================================================================
10545
10546 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10547
10548
10549 static struct TokenInfo global_setup_tokens[] =
10550 {
10551   {
10552     TYPE_STRING,
10553     &setup.player_name,                         "player_name"
10554   },
10555   {
10556     TYPE_SWITCH,
10557     &setup.multiple_users,                      "multiple_users"
10558   },
10559   {
10560     TYPE_SWITCH,
10561     &setup.sound,                               "sound"
10562   },
10563   {
10564     TYPE_SWITCH,
10565     &setup.sound_loops,                         "repeating_sound_loops"
10566   },
10567   {
10568     TYPE_SWITCH,
10569     &setup.sound_music,                         "background_music"
10570   },
10571   {
10572     TYPE_SWITCH,
10573     &setup.sound_simple,                        "simple_sound_effects"
10574   },
10575   {
10576     TYPE_SWITCH,
10577     &setup.toons,                               "toons"
10578   },
10579   {
10580     TYPE_SWITCH,
10581     &setup.global_animations,                   "global_animations"
10582   },
10583   {
10584     TYPE_SWITCH,
10585     &setup.scroll_delay,                        "scroll_delay"
10586   },
10587   {
10588     TYPE_SWITCH,
10589     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10590   },
10591   {
10592     TYPE_INTEGER,
10593     &setup.scroll_delay_value,                  "scroll_delay_value"
10594   },
10595   {
10596     TYPE_STRING,
10597     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10598   },
10599   {
10600     TYPE_INTEGER,
10601     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10602   },
10603   {
10604     TYPE_SWITCH,
10605     &setup.fade_screens,                        "fade_screens"
10606   },
10607   {
10608     TYPE_SWITCH,
10609     &setup.autorecord,                          "automatic_tape_recording"
10610   },
10611   {
10612     TYPE_SWITCH,
10613     &setup.autorecord_after_replay,             "autorecord_after_replay"
10614   },
10615   {
10616     TYPE_SWITCH,
10617     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10618   },
10619   {
10620     TYPE_SWITCH,
10621     &setup.show_titlescreen,                    "show_titlescreen"
10622   },
10623   {
10624     TYPE_SWITCH,
10625     &setup.quick_doors,                         "quick_doors"
10626   },
10627   {
10628     TYPE_SWITCH,
10629     &setup.team_mode,                           "team_mode"
10630   },
10631   {
10632     TYPE_SWITCH,
10633     &setup.handicap,                            "handicap"
10634   },
10635   {
10636     TYPE_SWITCH,
10637     &setup.skip_levels,                         "skip_levels"
10638   },
10639   {
10640     TYPE_SWITCH,
10641     &setup.increment_levels,                    "increment_levels"
10642   },
10643   {
10644     TYPE_SWITCH,
10645     &setup.auto_play_next_level,                "auto_play_next_level"
10646   },
10647   {
10648     TYPE_SWITCH,
10649     &setup.count_score_after_game,              "count_score_after_game"
10650   },
10651   {
10652     TYPE_SWITCH,
10653     &setup.show_scores_after_game,              "show_scores_after_game"
10654   },
10655   {
10656     TYPE_SWITCH,
10657     &setup.time_limit,                          "time_limit"
10658   },
10659   {
10660     TYPE_SWITCH,
10661     &setup.fullscreen,                          "fullscreen"
10662   },
10663   {
10664     TYPE_INTEGER,
10665     &setup.window_scaling_percent,              "window_scaling_percent"
10666   },
10667   {
10668     TYPE_STRING,
10669     &setup.window_scaling_quality,              "window_scaling_quality"
10670   },
10671   {
10672     TYPE_STRING,
10673     &setup.screen_rendering_mode,               "screen_rendering_mode"
10674   },
10675   {
10676     TYPE_STRING,
10677     &setup.vsync_mode,                          "vsync_mode"
10678   },
10679   {
10680     TYPE_SWITCH,
10681     &setup.ask_on_escape,                       "ask_on_escape"
10682   },
10683   {
10684     TYPE_SWITCH,
10685     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10686   },
10687   {
10688     TYPE_SWITCH,
10689     &setup.ask_on_game_over,                    "ask_on_game_over"
10690   },
10691   {
10692     TYPE_SWITCH,
10693     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10694   },
10695   {
10696     TYPE_SWITCH,
10697     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10698   },
10699   {
10700     TYPE_SWITCH,
10701     &setup.quick_switch,                        "quick_player_switch"
10702   },
10703   {
10704     TYPE_SWITCH,
10705     &setup.input_on_focus,                      "input_on_focus"
10706   },
10707   {
10708     TYPE_SWITCH,
10709     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10710   },
10711   {
10712     TYPE_SWITCH,
10713     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10714   },
10715   {
10716     TYPE_SWITCH,
10717     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10718   },
10719   {
10720     TYPE_SWITCH,
10721     &setup.game_speed_extended,                 "game_speed_extended"
10722   },
10723   {
10724     TYPE_INTEGER,
10725     &setup.game_frame_delay,                    "game_frame_delay"
10726   },
10727   {
10728     TYPE_SWITCH,
10729     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10730   },
10731   {
10732     TYPE_SWITCH,
10733     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10734   },
10735   {
10736     TYPE_SWITCH,
10737     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10738   },
10739   {
10740     TYPE_SWITCH3,
10741     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10742   },
10743   {
10744     TYPE_SWITCH,
10745     &setup.sp_show_border_elements,             "sp_show_border_elements"
10746   },
10747   {
10748     TYPE_SWITCH,
10749     &setup.small_game_graphics,                 "small_game_graphics"
10750   },
10751   {
10752     TYPE_SWITCH,
10753     &setup.show_load_save_buttons,              "show_load_save_buttons"
10754   },
10755   {
10756     TYPE_SWITCH,
10757     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10758   },
10759   {
10760     TYPE_STRING,
10761     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10762   },
10763   {
10764     TYPE_STRING,
10765     &setup.graphics_set,                        "graphics_set"
10766   },
10767   {
10768     TYPE_STRING,
10769     &setup.sounds_set,                          "sounds_set"
10770   },
10771   {
10772     TYPE_STRING,
10773     &setup.music_set,                           "music_set"
10774   },
10775   {
10776     TYPE_SWITCH3,
10777     &setup.override_level_graphics,             "override_level_graphics"
10778   },
10779   {
10780     TYPE_SWITCH3,
10781     &setup.override_level_sounds,               "override_level_sounds"
10782   },
10783   {
10784     TYPE_SWITCH3,
10785     &setup.override_level_music,                "override_level_music"
10786   },
10787   {
10788     TYPE_INTEGER,
10789     &setup.volume_simple,                       "volume_simple"
10790   },
10791   {
10792     TYPE_INTEGER,
10793     &setup.volume_loops,                        "volume_loops"
10794   },
10795   {
10796     TYPE_INTEGER,
10797     &setup.volume_music,                        "volume_music"
10798   },
10799   {
10800     TYPE_SWITCH,
10801     &setup.network_mode,                        "network_mode"
10802   },
10803   {
10804     TYPE_PLAYER,
10805     &setup.network_player_nr,                   "network_player"
10806   },
10807   {
10808     TYPE_STRING,
10809     &setup.network_server_hostname,             "network_server_hostname"
10810   },
10811   {
10812     TYPE_STRING,
10813     &setup.touch.control_type,                  "touch.control_type"
10814   },
10815   {
10816     TYPE_INTEGER,
10817     &setup.touch.move_distance,                 "touch.move_distance"
10818   },
10819   {
10820     TYPE_INTEGER,
10821     &setup.touch.drop_distance,                 "touch.drop_distance"
10822   },
10823   {
10824     TYPE_INTEGER,
10825     &setup.touch.transparency,                  "touch.transparency"
10826   },
10827   {
10828     TYPE_INTEGER,
10829     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10830   },
10831   {
10832     TYPE_INTEGER,
10833     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10834   },
10835   {
10836     TYPE_INTEGER,
10837     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10838   },
10839   {
10840     TYPE_INTEGER,
10841     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10842   },
10843   {
10844     TYPE_INTEGER,
10845     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10846   },
10847   {
10848     TYPE_INTEGER,
10849     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10850   },
10851   {
10852     TYPE_SWITCH,
10853     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10854   },
10855 };
10856
10857 static struct TokenInfo auto_setup_tokens[] =
10858 {
10859   {
10860     TYPE_INTEGER,
10861     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10862   },
10863 };
10864
10865 static struct TokenInfo server_setup_tokens[] =
10866 {
10867   {
10868     TYPE_STRING,
10869     &setup.player_uuid,                         "player_uuid"
10870   },
10871   {
10872     TYPE_INTEGER,
10873     &setup.player_version,                      "player_version"
10874   },
10875   {
10876     TYPE_SWITCH,
10877     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10878   },
10879   {
10880     TYPE_STRING,
10881     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10882   },
10883   {
10884     TYPE_STRING,
10885     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10886   },
10887   {
10888     TYPE_SWITCH,
10889     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10890   },
10891   {
10892     TYPE_SWITCH,
10893     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10894   },
10895   {
10896     TYPE_SWITCH,
10897     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10898   },
10899   {
10900     TYPE_SWITCH,
10901     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10902   },
10903   {
10904     TYPE_SWITCH,
10905     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10906   },
10907 };
10908
10909 static struct TokenInfo editor_setup_tokens[] =
10910 {
10911   {
10912     TYPE_SWITCH,
10913     &setup.editor.el_classic,                   "editor.el_classic"
10914   },
10915   {
10916     TYPE_SWITCH,
10917     &setup.editor.el_custom,                    "editor.el_custom"
10918   },
10919   {
10920     TYPE_SWITCH,
10921     &setup.editor.el_user_defined,              "editor.el_user_defined"
10922   },
10923   {
10924     TYPE_SWITCH,
10925     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10926   },
10927   {
10928     TYPE_SWITCH,
10929     &setup.editor.el_headlines,                 "editor.el_headlines"
10930   },
10931   {
10932     TYPE_SWITCH,
10933     &setup.editor.show_element_token,           "editor.show_element_token"
10934   },
10935   {
10936     TYPE_SWITCH,
10937     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10938   },
10939 };
10940
10941 static struct TokenInfo editor_cascade_setup_tokens[] =
10942 {
10943   {
10944     TYPE_SWITCH,
10945     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10946   },
10947   {
10948     TYPE_SWITCH,
10949     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10950   },
10951   {
10952     TYPE_SWITCH,
10953     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10954   },
10955   {
10956     TYPE_SWITCH,
10957     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10958   },
10959   {
10960     TYPE_SWITCH,
10961     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10962   },
10963   {
10964     TYPE_SWITCH,
10965     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10966   },
10967   {
10968     TYPE_SWITCH,
10969     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10970   },
10971   {
10972     TYPE_SWITCH,
10973     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10974   },
10975   {
10976     TYPE_SWITCH,
10977     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10978   },
10979   {
10980     TYPE_SWITCH,
10981     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10982   },
10983   {
10984     TYPE_SWITCH,
10985     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10986   },
10987   {
10988     TYPE_SWITCH,
10989     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10990   },
10991   {
10992     TYPE_SWITCH,
10993     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10994   },
10995   {
10996     TYPE_SWITCH,
10997     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10998   },
10999   {
11000     TYPE_SWITCH,
11001     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11002   },
11003   {
11004     TYPE_SWITCH,
11005     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11006   },
11007   {
11008     TYPE_SWITCH,
11009     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11010   },
11011   {
11012     TYPE_SWITCH,
11013     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11014   },
11015   {
11016     TYPE_SWITCH,
11017     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11018   },
11019   {
11020     TYPE_SWITCH,
11021     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11022   },
11023 };
11024
11025 static struct TokenInfo shortcut_setup_tokens[] =
11026 {
11027   {
11028     TYPE_KEY_X11,
11029     &setup.shortcut.save_game,                  "shortcut.save_game"
11030   },
11031   {
11032     TYPE_KEY_X11,
11033     &setup.shortcut.load_game,                  "shortcut.load_game"
11034   },
11035   {
11036     TYPE_KEY_X11,
11037     &setup.shortcut.restart_game,               "shortcut.restart_game"
11038   },
11039   {
11040     TYPE_KEY_X11,
11041     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11042   },
11043   {
11044     TYPE_KEY_X11,
11045     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11046   },
11047   {
11048     TYPE_KEY_X11,
11049     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11050   },
11051   {
11052     TYPE_KEY_X11,
11053     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11054   },
11055   {
11056     TYPE_KEY_X11,
11057     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11058   },
11059   {
11060     TYPE_KEY_X11,
11061     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11062   },
11063   {
11064     TYPE_KEY_X11,
11065     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11066   },
11067   {
11068     TYPE_KEY_X11,
11069     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11070   },
11071   {
11072     TYPE_KEY_X11,
11073     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11074   },
11075   {
11076     TYPE_KEY_X11,
11077     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11078   },
11079   {
11080     TYPE_KEY_X11,
11081     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11082   },
11083   {
11084     TYPE_KEY_X11,
11085     &setup.shortcut.tape_record,                "shortcut.tape_record"
11086   },
11087   {
11088     TYPE_KEY_X11,
11089     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11090   },
11091   {
11092     TYPE_KEY_X11,
11093     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11094   },
11095   {
11096     TYPE_KEY_X11,
11097     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11098   },
11099   {
11100     TYPE_KEY_X11,
11101     &setup.shortcut.sound_music,                "shortcut.sound_music"
11102   },
11103   {
11104     TYPE_KEY_X11,
11105     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11106   },
11107   {
11108     TYPE_KEY_X11,
11109     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11110   },
11111   {
11112     TYPE_KEY_X11,
11113     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11114   },
11115   {
11116     TYPE_KEY_X11,
11117     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11118   },
11119 };
11120
11121 static struct SetupInputInfo setup_input;
11122 static struct TokenInfo player_setup_tokens[] =
11123 {
11124   {
11125     TYPE_BOOLEAN,
11126     &setup_input.use_joystick,                  ".use_joystick"
11127   },
11128   {
11129     TYPE_STRING,
11130     &setup_input.joy.device_name,               ".joy.device_name"
11131   },
11132   {
11133     TYPE_INTEGER,
11134     &setup_input.joy.xleft,                     ".joy.xleft"
11135   },
11136   {
11137     TYPE_INTEGER,
11138     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11139   },
11140   {
11141     TYPE_INTEGER,
11142     &setup_input.joy.xright,                    ".joy.xright"
11143   },
11144   {
11145     TYPE_INTEGER,
11146     &setup_input.joy.yupper,                    ".joy.yupper"
11147   },
11148   {
11149     TYPE_INTEGER,
11150     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11151   },
11152   {
11153     TYPE_INTEGER,
11154     &setup_input.joy.ylower,                    ".joy.ylower"
11155   },
11156   {
11157     TYPE_INTEGER,
11158     &setup_input.joy.snap,                      ".joy.snap_field"
11159   },
11160   {
11161     TYPE_INTEGER,
11162     &setup_input.joy.drop,                      ".joy.place_bomb"
11163   },
11164   {
11165     TYPE_KEY_X11,
11166     &setup_input.key.left,                      ".key.move_left"
11167   },
11168   {
11169     TYPE_KEY_X11,
11170     &setup_input.key.right,                     ".key.move_right"
11171   },
11172   {
11173     TYPE_KEY_X11,
11174     &setup_input.key.up,                        ".key.move_up"
11175   },
11176   {
11177     TYPE_KEY_X11,
11178     &setup_input.key.down,                      ".key.move_down"
11179   },
11180   {
11181     TYPE_KEY_X11,
11182     &setup_input.key.snap,                      ".key.snap_field"
11183   },
11184   {
11185     TYPE_KEY_X11,
11186     &setup_input.key.drop,                      ".key.place_bomb"
11187   },
11188 };
11189
11190 static struct TokenInfo system_setup_tokens[] =
11191 {
11192   {
11193     TYPE_STRING,
11194     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11195   },
11196   {
11197     TYPE_STRING,
11198     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11199   },
11200   {
11201     TYPE_STRING,
11202     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11203   },
11204   {
11205     TYPE_INTEGER,
11206     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11207   },
11208 };
11209
11210 static struct TokenInfo internal_setup_tokens[] =
11211 {
11212   {
11213     TYPE_STRING,
11214     &setup.internal.program_title,              "program_title"
11215   },
11216   {
11217     TYPE_STRING,
11218     &setup.internal.program_version,            "program_version"
11219   },
11220   {
11221     TYPE_STRING,
11222     &setup.internal.program_author,             "program_author"
11223   },
11224   {
11225     TYPE_STRING,
11226     &setup.internal.program_email,              "program_email"
11227   },
11228   {
11229     TYPE_STRING,
11230     &setup.internal.program_website,            "program_website"
11231   },
11232   {
11233     TYPE_STRING,
11234     &setup.internal.program_copyright,          "program_copyright"
11235   },
11236   {
11237     TYPE_STRING,
11238     &setup.internal.program_company,            "program_company"
11239   },
11240   {
11241     TYPE_STRING,
11242     &setup.internal.program_icon_file,          "program_icon_file"
11243   },
11244   {
11245     TYPE_STRING,
11246     &setup.internal.default_graphics_set,       "default_graphics_set"
11247   },
11248   {
11249     TYPE_STRING,
11250     &setup.internal.default_sounds_set,         "default_sounds_set"
11251   },
11252   {
11253     TYPE_STRING,
11254     &setup.internal.default_music_set,          "default_music_set"
11255   },
11256   {
11257     TYPE_STRING,
11258     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11259   },
11260   {
11261     TYPE_STRING,
11262     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11263   },
11264   {
11265     TYPE_STRING,
11266     &setup.internal.fallback_music_file,        "fallback_music_file"
11267   },
11268   {
11269     TYPE_STRING,
11270     &setup.internal.default_level_series,       "default_level_series"
11271   },
11272   {
11273     TYPE_INTEGER,
11274     &setup.internal.default_window_width,       "default_window_width"
11275   },
11276   {
11277     TYPE_INTEGER,
11278     &setup.internal.default_window_height,      "default_window_height"
11279   },
11280   {
11281     TYPE_BOOLEAN,
11282     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11283   },
11284   {
11285     TYPE_BOOLEAN,
11286     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11287   },
11288   {
11289     TYPE_BOOLEAN,
11290     &setup.internal.create_user_levelset,       "create_user_levelset"
11291   },
11292   {
11293     TYPE_BOOLEAN,
11294     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11295   },
11296   {
11297     TYPE_BOOLEAN,
11298     &setup.internal.menu_game,                  "menu_game"
11299   },
11300   {
11301     TYPE_BOOLEAN,
11302     &setup.internal.menu_engines,               "menu_engines"
11303   },
11304   {
11305     TYPE_BOOLEAN,
11306     &setup.internal.menu_editor,                "menu_editor"
11307   },
11308   {
11309     TYPE_BOOLEAN,
11310     &setup.internal.menu_graphics,              "menu_graphics"
11311   },
11312   {
11313     TYPE_BOOLEAN,
11314     &setup.internal.menu_sound,                 "menu_sound"
11315   },
11316   {
11317     TYPE_BOOLEAN,
11318     &setup.internal.menu_artwork,               "menu_artwork"
11319   },
11320   {
11321     TYPE_BOOLEAN,
11322     &setup.internal.menu_input,                 "menu_input"
11323   },
11324   {
11325     TYPE_BOOLEAN,
11326     &setup.internal.menu_touch,                 "menu_touch"
11327   },
11328   {
11329     TYPE_BOOLEAN,
11330     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11331   },
11332   {
11333     TYPE_BOOLEAN,
11334     &setup.internal.menu_exit,                  "menu_exit"
11335   },
11336   {
11337     TYPE_BOOLEAN,
11338     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11339   },
11340   {
11341     TYPE_BOOLEAN,
11342     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11343   },
11344   {
11345     TYPE_BOOLEAN,
11346     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11347   },
11348   {
11349     TYPE_BOOLEAN,
11350     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11351   },
11352   {
11353     TYPE_BOOLEAN,
11354     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11355   },
11356   {
11357     TYPE_BOOLEAN,
11358     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11359   },
11360   {
11361     TYPE_BOOLEAN,
11362     &setup.internal.info_title,                 "info_title"
11363   },
11364   {
11365     TYPE_BOOLEAN,
11366     &setup.internal.info_elements,              "info_elements"
11367   },
11368   {
11369     TYPE_BOOLEAN,
11370     &setup.internal.info_music,                 "info_music"
11371   },
11372   {
11373     TYPE_BOOLEAN,
11374     &setup.internal.info_credits,               "info_credits"
11375   },
11376   {
11377     TYPE_BOOLEAN,
11378     &setup.internal.info_program,               "info_program"
11379   },
11380   {
11381     TYPE_BOOLEAN,
11382     &setup.internal.info_version,               "info_version"
11383   },
11384   {
11385     TYPE_BOOLEAN,
11386     &setup.internal.info_levelset,              "info_levelset"
11387   },
11388   {
11389     TYPE_BOOLEAN,
11390     &setup.internal.info_exit,                  "info_exit"
11391   },
11392 };
11393
11394 static struct TokenInfo debug_setup_tokens[] =
11395 {
11396   {
11397     TYPE_INTEGER,
11398     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11399   },
11400   {
11401     TYPE_INTEGER,
11402     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11403   },
11404   {
11405     TYPE_INTEGER,
11406     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11407   },
11408   {
11409     TYPE_INTEGER,
11410     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11411   },
11412   {
11413     TYPE_INTEGER,
11414     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11415   },
11416   {
11417     TYPE_INTEGER,
11418     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11419   },
11420   {
11421     TYPE_INTEGER,
11422     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11423   },
11424   {
11425     TYPE_INTEGER,
11426     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11427   },
11428   {
11429     TYPE_INTEGER,
11430     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11431   },
11432   {
11433     TYPE_INTEGER,
11434     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11435   },
11436   {
11437     TYPE_KEY_X11,
11438     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11439   },
11440   {
11441     TYPE_KEY_X11,
11442     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11443   },
11444   {
11445     TYPE_KEY_X11,
11446     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11447   },
11448   {
11449     TYPE_KEY_X11,
11450     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11451   },
11452   {
11453     TYPE_KEY_X11,
11454     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11455   },
11456   {
11457     TYPE_KEY_X11,
11458     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11459   },
11460   {
11461     TYPE_KEY_X11,
11462     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11463   },
11464   {
11465     TYPE_KEY_X11,
11466     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11467   },
11468   {
11469     TYPE_KEY_X11,
11470     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11471   },
11472   {
11473     TYPE_KEY_X11,
11474     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11475   },
11476   {
11477     TYPE_BOOLEAN,
11478     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11479   {
11480     TYPE_BOOLEAN,
11481     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11482   },
11483   {
11484     TYPE_BOOLEAN,
11485     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11486   },
11487   {
11488     TYPE_SWITCH3,
11489     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11490   },
11491   {
11492     TYPE_INTEGER,
11493     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11494   },
11495 };
11496
11497 static struct TokenInfo options_setup_tokens[] =
11498 {
11499   {
11500     TYPE_BOOLEAN,
11501     &setup.options.verbose,                     "options.verbose"
11502   },
11503   {
11504     TYPE_BOOLEAN,
11505     &setup.options.debug,                       "options.debug"
11506   },
11507   {
11508     TYPE_STRING,
11509     &setup.options.debug_mode,                  "options.debug_mode"
11510   },
11511 };
11512
11513 static void setSetupInfoToDefaults(struct SetupInfo *si)
11514 {
11515   int i;
11516
11517   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11518
11519   si->multiple_users = TRUE;
11520
11521   si->sound = TRUE;
11522   si->sound_loops = TRUE;
11523   si->sound_music = TRUE;
11524   si->sound_simple = TRUE;
11525   si->toons = TRUE;
11526   si->global_animations = TRUE;
11527   si->scroll_delay = TRUE;
11528   si->forced_scroll_delay = FALSE;
11529   si->scroll_delay_value = STD_SCROLL_DELAY;
11530   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11531   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11532   si->fade_screens = TRUE;
11533   si->autorecord = TRUE;
11534   si->autorecord_after_replay = TRUE;
11535   si->auto_pause_on_start = FALSE;
11536   si->show_titlescreen = TRUE;
11537   si->quick_doors = FALSE;
11538   si->team_mode = FALSE;
11539   si->handicap = TRUE;
11540   si->skip_levels = TRUE;
11541   si->increment_levels = TRUE;
11542   si->auto_play_next_level = TRUE;
11543   si->count_score_after_game = TRUE;
11544   si->show_scores_after_game = TRUE;
11545   si->time_limit = TRUE;
11546   si->fullscreen = FALSE;
11547   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11548   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11549   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11550   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11551   si->ask_on_escape = TRUE;
11552   si->ask_on_escape_editor = TRUE;
11553   si->ask_on_game_over = TRUE;
11554   si->ask_on_quit_game = TRUE;
11555   si->ask_on_quit_program = TRUE;
11556   si->quick_switch = FALSE;
11557   si->input_on_focus = FALSE;
11558   si->prefer_aga_graphics = TRUE;
11559   si->prefer_lowpass_sounds = FALSE;
11560   si->prefer_extra_panel_items = TRUE;
11561   si->game_speed_extended = FALSE;
11562   si->game_frame_delay = GAME_FRAME_DELAY;
11563   si->bd_skip_uncovering = FALSE;
11564   si->bd_skip_hatching = FALSE;
11565   si->bd_scroll_delay = TRUE;
11566   si->bd_smooth_movements = AUTO;
11567   si->sp_show_border_elements = FALSE;
11568   si->small_game_graphics = FALSE;
11569   si->show_load_save_buttons = FALSE;
11570   si->show_undo_redo_buttons = FALSE;
11571   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11572
11573   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11574   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11575   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11576
11577   si->override_level_graphics = FALSE;
11578   si->override_level_sounds = FALSE;
11579   si->override_level_music = FALSE;
11580
11581   si->volume_simple = 100;              // percent
11582   si->volume_loops = 100;               // percent
11583   si->volume_music = 100;               // percent
11584
11585   si->network_mode = FALSE;
11586   si->network_player_nr = 0;            // first player
11587   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11588
11589   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11590   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11591   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11592   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11593   si->touch.draw_outlined = TRUE;
11594   si->touch.draw_pressed = TRUE;
11595
11596   for (i = 0; i < 2; i++)
11597   {
11598     char *default_grid_button[6][2] =
11599     {
11600       { "      ", "  ^^  " },
11601       { "      ", "  ^^  " },
11602       { "      ", "<<  >>" },
11603       { "      ", "<<  >>" },
11604       { "111222", "  vv  " },
11605       { "111222", "  vv  " }
11606     };
11607     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11608     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11609     int min_xsize = MIN(6, grid_xsize);
11610     int min_ysize = MIN(6, grid_ysize);
11611     int startx = grid_xsize - min_xsize;
11612     int starty = grid_ysize - min_ysize;
11613     int x, y;
11614
11615     // virtual buttons grid can only be set to defaults if video is initialized
11616     // (this will be repeated if virtual buttons are not loaded from setup file)
11617     if (video.initialized)
11618     {
11619       si->touch.grid_xsize[i] = grid_xsize;
11620       si->touch.grid_ysize[i] = grid_ysize;
11621     }
11622     else
11623     {
11624       si->touch.grid_xsize[i] = -1;
11625       si->touch.grid_ysize[i] = -1;
11626     }
11627
11628     for (x = 0; x < MAX_GRID_XSIZE; x++)
11629       for (y = 0; y < MAX_GRID_YSIZE; y++)
11630         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11631
11632     for (x = 0; x < min_xsize; x++)
11633       for (y = 0; y < min_ysize; y++)
11634         si->touch.grid_button[i][x][starty + y] =
11635           default_grid_button[y][0][x];
11636
11637     for (x = 0; x < min_xsize; x++)
11638       for (y = 0; y < min_ysize; y++)
11639         si->touch.grid_button[i][startx + x][starty + y] =
11640           default_grid_button[y][1][x];
11641   }
11642
11643   si->touch.grid_initialized            = video.initialized;
11644
11645   si->touch.overlay_buttons             = FALSE;
11646
11647   si->editor.el_boulderdash             = TRUE;
11648   si->editor.el_boulderdash_native      = TRUE;
11649   si->editor.el_boulderdash_effects     = TRUE;
11650   si->editor.el_emerald_mine            = TRUE;
11651   si->editor.el_emerald_mine_club       = TRUE;
11652   si->editor.el_more                    = TRUE;
11653   si->editor.el_sokoban                 = TRUE;
11654   si->editor.el_supaplex                = TRUE;
11655   si->editor.el_diamond_caves           = TRUE;
11656   si->editor.el_dx_boulderdash          = TRUE;
11657
11658   si->editor.el_mirror_magic            = TRUE;
11659   si->editor.el_deflektor               = TRUE;
11660
11661   si->editor.el_chars                   = TRUE;
11662   si->editor.el_steel_chars             = TRUE;
11663
11664   si->editor.el_classic                 = TRUE;
11665   si->editor.el_custom                  = TRUE;
11666
11667   si->editor.el_user_defined            = FALSE;
11668   si->editor.el_dynamic                 = TRUE;
11669
11670   si->editor.el_headlines               = TRUE;
11671
11672   si->editor.show_element_token         = FALSE;
11673
11674   si->editor.show_read_only_warning     = TRUE;
11675
11676   si->editor.use_template_for_new_levels = TRUE;
11677
11678   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11679   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11680   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11681   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11682   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11683
11684   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11685   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11686   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11687   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11688   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11689
11690   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11691   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11692   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11693   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11694   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11695   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11696
11697   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11698   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11699   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11700
11701   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11702   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11703   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11704   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11705
11706   for (i = 0; i < MAX_PLAYERS; i++)
11707   {
11708     si->input[i].use_joystick = FALSE;
11709     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11710     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11711     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11712     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11713     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11714     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11715     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11716     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11717     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11718     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11719     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11720     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11721     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11722     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11723     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11724   }
11725
11726   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11727   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11728   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11729   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11730
11731   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11732   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11733   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11734   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11735   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11736   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11737   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11738
11739   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11740
11741   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11742   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11743   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11744
11745   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11746   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11747   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11748
11749   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11750   si->internal.choose_from_top_leveldir = FALSE;
11751   si->internal.show_scaling_in_title = TRUE;
11752   si->internal.create_user_levelset = TRUE;
11753   si->internal.info_screens_from_main = FALSE;
11754
11755   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11756   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11757
11758   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11759   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11760   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11761   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11762   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11763   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11764   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11765   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11766   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11767   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11768
11769   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11770   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11771   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11772   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11773   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11774   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11775   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11776   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11777   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11778   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11779
11780   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11781   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11782
11783   si->debug.show_frames_per_second = FALSE;
11784
11785   si->debug.xsn_mode = AUTO;
11786   si->debug.xsn_percent = 0;
11787
11788   si->options.verbose = FALSE;
11789   si->options.debug = FALSE;
11790   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11791
11792 #if defined(PLATFORM_ANDROID)
11793   si->fullscreen = TRUE;
11794   si->touch.overlay_buttons = TRUE;
11795 #endif
11796
11797   setHideSetupEntry(&setup.debug.xsn_mode);
11798 }
11799
11800 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11801 {
11802   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11803 }
11804
11805 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11806 {
11807   si->player_uuid = NULL;       // (will be set later)
11808   si->player_version = 1;       // (will be set later)
11809
11810   si->use_api_server = TRUE;
11811   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11812   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11813   si->ask_for_uploading_tapes = TRUE;
11814   si->ask_for_remaining_tapes = FALSE;
11815   si->provide_uploading_tapes = TRUE;
11816   si->ask_for_using_api_server = TRUE;
11817   si->has_remaining_tapes = FALSE;
11818 }
11819
11820 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11821 {
11822   si->editor_cascade.el_bd              = TRUE;
11823   si->editor_cascade.el_bd_native       = TRUE;
11824   si->editor_cascade.el_bd_effects      = FALSE;
11825   si->editor_cascade.el_em              = TRUE;
11826   si->editor_cascade.el_emc             = TRUE;
11827   si->editor_cascade.el_rnd             = TRUE;
11828   si->editor_cascade.el_sb              = TRUE;
11829   si->editor_cascade.el_sp              = TRUE;
11830   si->editor_cascade.el_dc              = TRUE;
11831   si->editor_cascade.el_dx              = TRUE;
11832
11833   si->editor_cascade.el_mm              = TRUE;
11834   si->editor_cascade.el_df              = TRUE;
11835
11836   si->editor_cascade.el_chars           = FALSE;
11837   si->editor_cascade.el_steel_chars     = FALSE;
11838   si->editor_cascade.el_ce              = FALSE;
11839   si->editor_cascade.el_ge              = FALSE;
11840   si->editor_cascade.el_es              = FALSE;
11841   si->editor_cascade.el_ref             = FALSE;
11842   si->editor_cascade.el_user            = FALSE;
11843   si->editor_cascade.el_dynamic         = FALSE;
11844 }
11845
11846 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11847
11848 static char *getHideSetupToken(void *setup_value)
11849 {
11850   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11851
11852   if (setup_value != NULL)
11853     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11854
11855   return hide_setup_token;
11856 }
11857
11858 void setHideSetupEntry(void *setup_value)
11859 {
11860   char *hide_setup_token = getHideSetupToken(setup_value);
11861
11862   if (hide_setup_hash == NULL)
11863     hide_setup_hash = newSetupFileHash();
11864
11865   if (setup_value != NULL)
11866     setHashEntry(hide_setup_hash, hide_setup_token, "");
11867 }
11868
11869 void removeHideSetupEntry(void *setup_value)
11870 {
11871   char *hide_setup_token = getHideSetupToken(setup_value);
11872
11873   if (setup_value != NULL)
11874     removeHashEntry(hide_setup_hash, hide_setup_token);
11875 }
11876
11877 boolean hideSetupEntry(void *setup_value)
11878 {
11879   char *hide_setup_token = getHideSetupToken(setup_value);
11880
11881   return (setup_value != NULL &&
11882           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11883 }
11884
11885 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11886                                       struct TokenInfo *token_info,
11887                                       int token_nr, char *token_text)
11888 {
11889   char *token_hide_text = getStringCat2(token_text, ".hide");
11890   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11891
11892   // set the value of this setup option in the setup option structure
11893   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11894
11895   // check if this setup option should be hidden in the setup menu
11896   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11897     setHideSetupEntry(token_info[token_nr].value);
11898
11899   free(token_hide_text);
11900 }
11901
11902 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11903                                       struct TokenInfo *token_info,
11904                                       int token_nr)
11905 {
11906   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11907                             token_info[token_nr].text);
11908 }
11909
11910 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11911 {
11912   int i, pnr;
11913
11914   if (!setup_file_hash)
11915     return;
11916
11917   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11918     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11919
11920   setup.touch.grid_initialized = TRUE;
11921   for (i = 0; i < 2; i++)
11922   {
11923     int grid_xsize = setup.touch.grid_xsize[i];
11924     int grid_ysize = setup.touch.grid_ysize[i];
11925     int x, y;
11926
11927     // if virtual buttons are not loaded from setup file, repeat initializing
11928     // virtual buttons grid with default values later when video is initialized
11929     if (grid_xsize == -1 ||
11930         grid_ysize == -1)
11931     {
11932       setup.touch.grid_initialized = FALSE;
11933
11934       continue;
11935     }
11936
11937     for (y = 0; y < grid_ysize; y++)
11938     {
11939       char token_string[MAX_LINE_LEN];
11940
11941       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11942
11943       char *value_string = getHashEntry(setup_file_hash, token_string);
11944
11945       if (value_string == NULL)
11946         continue;
11947
11948       for (x = 0; x < grid_xsize; x++)
11949       {
11950         char c = value_string[x];
11951
11952         setup.touch.grid_button[i][x][y] =
11953           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11954       }
11955     }
11956   }
11957
11958   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11959     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11960
11961   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11962     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11963
11964   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11965   {
11966     char prefix[30];
11967
11968     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11969
11970     setup_input = setup.input[pnr];
11971     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11972     {
11973       char full_token[100];
11974
11975       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11976       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11977                                 full_token);
11978     }
11979     setup.input[pnr] = setup_input;
11980   }
11981
11982   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11983     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11984
11985   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11986     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11987
11988   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11989     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11990
11991   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11992     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11993
11994   setHideRelatedSetupEntries();
11995 }
11996
11997 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11998 {
11999   int i;
12000
12001   if (!setup_file_hash)
12002     return;
12003
12004   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12005     setSetupInfo(auto_setup_tokens, i,
12006                  getHashEntry(setup_file_hash,
12007                               auto_setup_tokens[i].text));
12008 }
12009
12010 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12011 {
12012   int i;
12013
12014   if (!setup_file_hash)
12015     return;
12016
12017   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12018     setSetupInfo(server_setup_tokens, i,
12019                  getHashEntry(setup_file_hash,
12020                               server_setup_tokens[i].text));
12021 }
12022
12023 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12024 {
12025   int i;
12026
12027   if (!setup_file_hash)
12028     return;
12029
12030   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12031     setSetupInfo(editor_cascade_setup_tokens, i,
12032                  getHashEntry(setup_file_hash,
12033                               editor_cascade_setup_tokens[i].text));
12034 }
12035
12036 void LoadUserNames(void)
12037 {
12038   int last_user_nr = user.nr;
12039   int i;
12040
12041   if (global.user_names != NULL)
12042   {
12043     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12044       checked_free(global.user_names[i]);
12045
12046     checked_free(global.user_names);
12047   }
12048
12049   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12050
12051   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12052   {
12053     user.nr = i;
12054
12055     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12056
12057     if (setup_file_hash)
12058     {
12059       char *player_name = getHashEntry(setup_file_hash, "player_name");
12060
12061       global.user_names[i] = getFixedUserName(player_name);
12062
12063       freeSetupFileHash(setup_file_hash);
12064     }
12065
12066     if (global.user_names[i] == NULL)
12067       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12068   }
12069
12070   user.nr = last_user_nr;
12071 }
12072
12073 void LoadSetupFromFilename(char *filename)
12074 {
12075   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12076
12077   if (setup_file_hash)
12078   {
12079     decodeSetupFileHash_Default(setup_file_hash);
12080
12081     freeSetupFileHash(setup_file_hash);
12082   }
12083   else
12084   {
12085     Debug("setup", "using default setup values");
12086   }
12087 }
12088
12089 static void LoadSetup_SpecialPostProcessing(void)
12090 {
12091   char *player_name_new;
12092
12093   // needed to work around problems with fixed length strings
12094   player_name_new = getFixedUserName(setup.player_name);
12095   free(setup.player_name);
12096   setup.player_name = player_name_new;
12097
12098   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12099   if (setup.scroll_delay == FALSE)
12100   {
12101     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12102     setup.scroll_delay = TRUE;                  // now always "on"
12103   }
12104
12105   // make sure that scroll delay value stays inside valid range
12106   setup.scroll_delay_value =
12107     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12108 }
12109
12110 void LoadSetup_Default(void)
12111 {
12112   char *filename;
12113
12114   // always start with reliable default values
12115   setSetupInfoToDefaults(&setup);
12116
12117   // try to load setup values from default setup file
12118   filename = getDefaultSetupFilename();
12119
12120   if (fileExists(filename))
12121     LoadSetupFromFilename(filename);
12122
12123   // try to load setup values from platform setup file
12124   filename = getPlatformSetupFilename();
12125
12126   if (fileExists(filename))
12127     LoadSetupFromFilename(filename);
12128
12129   // try to load setup values from user setup file
12130   filename = getSetupFilename();
12131
12132   LoadSetupFromFilename(filename);
12133
12134   LoadSetup_SpecialPostProcessing();
12135 }
12136
12137 void LoadSetup_AutoSetup(void)
12138 {
12139   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12140   SetupFileHash *setup_file_hash = NULL;
12141
12142   // always start with reliable default values
12143   setSetupInfoToDefaults_AutoSetup(&setup);
12144
12145   setup_file_hash = loadSetupFileHash(filename);
12146
12147   if (setup_file_hash)
12148   {
12149     decodeSetupFileHash_AutoSetup(setup_file_hash);
12150
12151     freeSetupFileHash(setup_file_hash);
12152   }
12153
12154   free(filename);
12155 }
12156
12157 void LoadSetup_ServerSetup(void)
12158 {
12159   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12160   SetupFileHash *setup_file_hash = NULL;
12161
12162   // always start with reliable default values
12163   setSetupInfoToDefaults_ServerSetup(&setup);
12164
12165   setup_file_hash = loadSetupFileHash(filename);
12166
12167   if (setup_file_hash)
12168   {
12169     decodeSetupFileHash_ServerSetup(setup_file_hash);
12170
12171     freeSetupFileHash(setup_file_hash);
12172   }
12173
12174   free(filename);
12175
12176   if (setup.player_uuid == NULL)
12177   {
12178     // player UUID does not yet exist in setup file
12179     setup.player_uuid = getStringCopy(getUUID());
12180     setup.player_version = 2;
12181
12182     SaveSetup_ServerSetup();
12183   }
12184 }
12185
12186 void LoadSetup_EditorCascade(void)
12187 {
12188   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12189   SetupFileHash *setup_file_hash = NULL;
12190
12191   // always start with reliable default values
12192   setSetupInfoToDefaults_EditorCascade(&setup);
12193
12194   setup_file_hash = loadSetupFileHash(filename);
12195
12196   if (setup_file_hash)
12197   {
12198     decodeSetupFileHash_EditorCascade(setup_file_hash);
12199
12200     freeSetupFileHash(setup_file_hash);
12201   }
12202
12203   free(filename);
12204 }
12205
12206 void LoadSetup(void)
12207 {
12208   LoadSetup_Default();
12209   LoadSetup_AutoSetup();
12210   LoadSetup_ServerSetup();
12211   LoadSetup_EditorCascade();
12212 }
12213
12214 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12215                                            char *mapping_line)
12216 {
12217   char mapping_guid[MAX_LINE_LEN];
12218   char *mapping_start, *mapping_end;
12219
12220   // get GUID from game controller mapping line: copy complete line
12221   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12222   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12223
12224   // get GUID from game controller mapping line: cut after GUID part
12225   mapping_start = strchr(mapping_guid, ',');
12226   if (mapping_start != NULL)
12227     *mapping_start = '\0';
12228
12229   // cut newline from game controller mapping line
12230   mapping_end = strchr(mapping_line, '\n');
12231   if (mapping_end != NULL)
12232     *mapping_end = '\0';
12233
12234   // add mapping entry to game controller mappings hash
12235   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12236 }
12237
12238 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12239                                                  char *filename)
12240 {
12241   FILE *file;
12242
12243   if (!(file = fopen(filename, MODE_READ)))
12244   {
12245     Warn("cannot read game controller mappings file '%s'", filename);
12246
12247     return;
12248   }
12249
12250   while (!feof(file))
12251   {
12252     char line[MAX_LINE_LEN];
12253
12254     if (!fgets(line, MAX_LINE_LEN, file))
12255       break;
12256
12257     addGameControllerMappingToHash(mappings_hash, line);
12258   }
12259
12260   fclose(file);
12261 }
12262
12263 void SaveSetup_Default(void)
12264 {
12265   char *filename = getSetupFilename();
12266   FILE *file;
12267   int i, pnr;
12268
12269   InitUserDataDirectory();
12270
12271   if (!(file = fopen(filename, MODE_WRITE)))
12272   {
12273     Warn("cannot write setup file '%s'", filename);
12274
12275     return;
12276   }
12277
12278   fprintFileHeader(file, SETUP_FILENAME);
12279
12280   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12281   {
12282     // just to make things nicer :)
12283     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12284         global_setup_tokens[i].value == &setup.sound                    ||
12285         global_setup_tokens[i].value == &setup.graphics_set             ||
12286         global_setup_tokens[i].value == &setup.volume_simple            ||
12287         global_setup_tokens[i].value == &setup.network_mode             ||
12288         global_setup_tokens[i].value == &setup.touch.control_type       ||
12289         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12290         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12291       fprintf(file, "\n");
12292
12293     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12294   }
12295
12296   for (i = 0; i < 2; i++)
12297   {
12298     int grid_xsize = setup.touch.grid_xsize[i];
12299     int grid_ysize = setup.touch.grid_ysize[i];
12300     int x, y;
12301
12302     fprintf(file, "\n");
12303
12304     for (y = 0; y < grid_ysize; y++)
12305     {
12306       char token_string[MAX_LINE_LEN];
12307       char value_string[MAX_LINE_LEN];
12308
12309       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12310
12311       for (x = 0; x < grid_xsize; x++)
12312       {
12313         char c = setup.touch.grid_button[i][x][y];
12314
12315         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12316       }
12317
12318       value_string[grid_xsize] = '\0';
12319
12320       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12321     }
12322   }
12323
12324   fprintf(file, "\n");
12325   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12326     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12327
12328   fprintf(file, "\n");
12329   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12330     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12331
12332   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12333   {
12334     char prefix[30];
12335
12336     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12337     fprintf(file, "\n");
12338
12339     setup_input = setup.input[pnr];
12340     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12341       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12342   }
12343
12344   fprintf(file, "\n");
12345   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12346     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12347
12348   // (internal setup values not saved to user setup file)
12349
12350   fprintf(file, "\n");
12351   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12352     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12353         setup.debug.xsn_mode != AUTO)
12354       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12355
12356   fprintf(file, "\n");
12357   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12358     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12359
12360   fclose(file);
12361
12362   SetFilePermissions(filename, PERMS_PRIVATE);
12363 }
12364
12365 void SaveSetup_AutoSetup(void)
12366 {
12367   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12368   FILE *file;
12369   int i;
12370
12371   InitUserDataDirectory();
12372
12373   if (!(file = fopen(filename, MODE_WRITE)))
12374   {
12375     Warn("cannot write auto setup file '%s'", filename);
12376
12377     free(filename);
12378
12379     return;
12380   }
12381
12382   fprintFileHeader(file, AUTOSETUP_FILENAME);
12383
12384   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12385     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12386
12387   fclose(file);
12388
12389   SetFilePermissions(filename, PERMS_PRIVATE);
12390
12391   free(filename);
12392 }
12393
12394 void SaveSetup_ServerSetup(void)
12395 {
12396   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12397   FILE *file;
12398   int i;
12399
12400   InitUserDataDirectory();
12401
12402   if (!(file = fopen(filename, MODE_WRITE)))
12403   {
12404     Warn("cannot write server setup file '%s'", filename);
12405
12406     free(filename);
12407
12408     return;
12409   }
12410
12411   fprintFileHeader(file, SERVERSETUP_FILENAME);
12412
12413   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12414   {
12415     // just to make things nicer :)
12416     if (server_setup_tokens[i].value == &setup.use_api_server)
12417       fprintf(file, "\n");
12418
12419     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12420   }
12421
12422   fclose(file);
12423
12424   SetFilePermissions(filename, PERMS_PRIVATE);
12425
12426   free(filename);
12427 }
12428
12429 void SaveSetup_EditorCascade(void)
12430 {
12431   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12432   FILE *file;
12433   int i;
12434
12435   InitUserDataDirectory();
12436
12437   if (!(file = fopen(filename, MODE_WRITE)))
12438   {
12439     Warn("cannot write editor cascade state file '%s'", filename);
12440
12441     free(filename);
12442
12443     return;
12444   }
12445
12446   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12447
12448   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12449     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12450
12451   fclose(file);
12452
12453   SetFilePermissions(filename, PERMS_PRIVATE);
12454
12455   free(filename);
12456 }
12457
12458 void SaveSetup(void)
12459 {
12460   SaveSetup_Default();
12461   SaveSetup_AutoSetup();
12462   SaveSetup_ServerSetup();
12463   SaveSetup_EditorCascade();
12464 }
12465
12466 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12467                                                   char *filename)
12468 {
12469   FILE *file;
12470
12471   if (!(file = fopen(filename, MODE_WRITE)))
12472   {
12473     Warn("cannot write game controller mappings file '%s'", filename);
12474
12475     return;
12476   }
12477
12478   BEGIN_HASH_ITERATION(mappings_hash, itr)
12479   {
12480     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12481   }
12482   END_HASH_ITERATION(mappings_hash, itr)
12483
12484   fclose(file);
12485 }
12486
12487 void SaveSetup_AddGameControllerMapping(char *mapping)
12488 {
12489   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12490   SetupFileHash *mappings_hash = newSetupFileHash();
12491
12492   InitUserDataDirectory();
12493
12494   // load existing personal game controller mappings
12495   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12496
12497   // add new mapping to personal game controller mappings
12498   addGameControllerMappingToHash(mappings_hash, mapping);
12499
12500   // save updated personal game controller mappings
12501   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12502
12503   freeSetupFileHash(mappings_hash);
12504   free(filename);
12505 }
12506
12507 void LoadCustomElementDescriptions(void)
12508 {
12509   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12510   SetupFileHash *setup_file_hash;
12511   int i;
12512
12513   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12514   {
12515     if (element_info[i].custom_description != NULL)
12516     {
12517       free(element_info[i].custom_description);
12518       element_info[i].custom_description = NULL;
12519     }
12520   }
12521
12522   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12523     return;
12524
12525   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12526   {
12527     char *token = getStringCat2(element_info[i].token_name, ".name");
12528     char *value = getHashEntry(setup_file_hash, token);
12529
12530     if (value != NULL)
12531       element_info[i].custom_description = getStringCopy(value);
12532
12533     free(token);
12534   }
12535
12536   freeSetupFileHash(setup_file_hash);
12537 }
12538
12539 static int getElementFromToken(char *token)
12540 {
12541   char *value = getHashEntry(element_token_hash, token);
12542
12543   if (value != NULL)
12544     return atoi(value);
12545
12546   Warn("unknown element token '%s'", token);
12547
12548   return EL_UNDEFINED;
12549 }
12550
12551 void FreeGlobalAnimEventInfo(void)
12552 {
12553   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12554
12555   if (gaei->event_list == NULL)
12556     return;
12557
12558   int i;
12559
12560   for (i = 0; i < gaei->num_event_lists; i++)
12561   {
12562     checked_free(gaei->event_list[i]->event_value);
12563     checked_free(gaei->event_list[i]);
12564   }
12565
12566   checked_free(gaei->event_list);
12567
12568   gaei->event_list = NULL;
12569   gaei->num_event_lists = 0;
12570 }
12571
12572 static int AddGlobalAnimEventList(void)
12573 {
12574   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12575   int list_pos = gaei->num_event_lists++;
12576
12577   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12578                                      sizeof(struct GlobalAnimEventListInfo *));
12579
12580   gaei->event_list[list_pos] =
12581     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12582
12583   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12584
12585   gaeli->event_value = NULL;
12586   gaeli->num_event_values = 0;
12587
12588   return list_pos;
12589 }
12590
12591 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12592 {
12593   // do not add empty global animation events
12594   if (event_value == ANIM_EVENT_NONE)
12595     return list_pos;
12596
12597   // if list position is undefined, create new list
12598   if (list_pos == ANIM_EVENT_UNDEFINED)
12599     list_pos = AddGlobalAnimEventList();
12600
12601   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12602   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12603   int value_pos = gaeli->num_event_values++;
12604
12605   gaeli->event_value = checked_realloc(gaeli->event_value,
12606                                        gaeli->num_event_values * sizeof(int *));
12607
12608   gaeli->event_value[value_pos] = event_value;
12609
12610   return list_pos;
12611 }
12612
12613 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12614 {
12615   if (list_pos == ANIM_EVENT_UNDEFINED)
12616     return ANIM_EVENT_NONE;
12617
12618   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12619   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12620
12621   return gaeli->event_value[value_pos];
12622 }
12623
12624 int GetGlobalAnimEventValueCount(int list_pos)
12625 {
12626   if (list_pos == ANIM_EVENT_UNDEFINED)
12627     return 0;
12628
12629   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12630   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12631
12632   return gaeli->num_event_values;
12633 }
12634
12635 // This function checks if a string <s> of the format "string1, string2, ..."
12636 // exactly contains a string <s_contained>.
12637
12638 static boolean string_has_parameter(char *s, char *s_contained)
12639 {
12640   char *substring;
12641
12642   if (s == NULL || s_contained == NULL)
12643     return FALSE;
12644
12645   if (strlen(s_contained) > strlen(s))
12646     return FALSE;
12647
12648   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12649   {
12650     char next_char = s[strlen(s_contained)];
12651
12652     // check if next character is delimiter or whitespace
12653     if (next_char == ',' || next_char == '\0' ||
12654         next_char == ' ' || next_char == '\t')
12655       return TRUE;
12656   }
12657
12658   // check if string contains another parameter string after a comma
12659   substring = strchr(s, ',');
12660   if (substring == NULL)        // string does not contain a comma
12661     return FALSE;
12662
12663   // advance string pointer to next character after the comma
12664   substring++;
12665
12666   // skip potential whitespaces after the comma
12667   while (*substring == ' ' || *substring == '\t')
12668     substring++;
12669
12670   return string_has_parameter(substring, s_contained);
12671 }
12672
12673 static int get_anim_parameter_value_ce(char *s)
12674 {
12675   char *s_ptr = s;
12676   char *pattern_1 = "ce_change:custom_";
12677   char *pattern_2 = ".page_";
12678   int pattern_1_len = strlen(pattern_1);
12679   char *matching_char = strstr(s_ptr, pattern_1);
12680   int result = ANIM_EVENT_NONE;
12681
12682   if (matching_char == NULL)
12683     return ANIM_EVENT_NONE;
12684
12685   result = ANIM_EVENT_CE_CHANGE;
12686
12687   s_ptr = matching_char + pattern_1_len;
12688
12689   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12690   if (*s_ptr >= '0' && *s_ptr <= '9')
12691   {
12692     int gic_ce_nr = (*s_ptr++ - '0');
12693
12694     if (*s_ptr >= '0' && *s_ptr <= '9')
12695     {
12696       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12697
12698       if (*s_ptr >= '0' && *s_ptr <= '9')
12699         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12700     }
12701
12702     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12703       return ANIM_EVENT_NONE;
12704
12705     // custom element stored as 0 to 255
12706     gic_ce_nr--;
12707
12708     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12709   }
12710   else
12711   {
12712     // invalid custom element number specified
12713
12714     return ANIM_EVENT_NONE;
12715   }
12716
12717   // check for change page number ("page_X" or "page_XX") (optional)
12718   if (strPrefix(s_ptr, pattern_2))
12719   {
12720     s_ptr += strlen(pattern_2);
12721
12722     if (*s_ptr >= '0' && *s_ptr <= '9')
12723     {
12724       int gic_page_nr = (*s_ptr++ - '0');
12725
12726       if (*s_ptr >= '0' && *s_ptr <= '9')
12727         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12728
12729       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12730         return ANIM_EVENT_NONE;
12731
12732       // change page stored as 1 to 32 (0 means "all change pages")
12733
12734       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12735     }
12736     else
12737     {
12738       // invalid animation part number specified
12739
12740       return ANIM_EVENT_NONE;
12741     }
12742   }
12743
12744   // discard result if next character is neither delimiter nor whitespace
12745   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12746         *s_ptr == ' ' || *s_ptr == '\t'))
12747     return ANIM_EVENT_NONE;
12748
12749   return result;
12750 }
12751
12752 static int get_anim_parameter_value(char *s)
12753 {
12754   int event_value[] =
12755   {
12756     ANIM_EVENT_CLICK,
12757     ANIM_EVENT_INIT,
12758     ANIM_EVENT_START,
12759     ANIM_EVENT_END,
12760     ANIM_EVENT_POST
12761   };
12762   char *pattern_1[] =
12763   {
12764     "click:anim_",
12765     "init:anim_",
12766     "start:anim_",
12767     "end:anim_",
12768     "post:anim_"
12769   };
12770   char *pattern_2 = ".part_";
12771   char *matching_char = NULL;
12772   char *s_ptr = s;
12773   int pattern_1_len = 0;
12774   int result = ANIM_EVENT_NONE;
12775   int i;
12776
12777   result = get_anim_parameter_value_ce(s);
12778
12779   if (result != ANIM_EVENT_NONE)
12780     return result;
12781
12782   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12783   {
12784     matching_char = strstr(s_ptr, pattern_1[i]);
12785     pattern_1_len = strlen(pattern_1[i]);
12786     result = event_value[i];
12787
12788     if (matching_char != NULL)
12789       break;
12790   }
12791
12792   if (matching_char == NULL)
12793     return ANIM_EVENT_NONE;
12794
12795   s_ptr = matching_char + pattern_1_len;
12796
12797   // check for main animation number ("anim_X" or "anim_XX")
12798   if (*s_ptr >= '0' && *s_ptr <= '9')
12799   {
12800     int gic_anim_nr = (*s_ptr++ - '0');
12801
12802     if (*s_ptr >= '0' && *s_ptr <= '9')
12803       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12804
12805     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12806       return ANIM_EVENT_NONE;
12807
12808     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12809   }
12810   else
12811   {
12812     // invalid main animation number specified
12813
12814     return ANIM_EVENT_NONE;
12815   }
12816
12817   // check for animation part number ("part_X" or "part_XX") (optional)
12818   if (strPrefix(s_ptr, pattern_2))
12819   {
12820     s_ptr += strlen(pattern_2);
12821
12822     if (*s_ptr >= '0' && *s_ptr <= '9')
12823     {
12824       int gic_part_nr = (*s_ptr++ - '0');
12825
12826       if (*s_ptr >= '0' && *s_ptr <= '9')
12827         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12828
12829       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12830         return ANIM_EVENT_NONE;
12831
12832       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12833     }
12834     else
12835     {
12836       // invalid animation part number specified
12837
12838       return ANIM_EVENT_NONE;
12839     }
12840   }
12841
12842   // discard result if next character is neither delimiter nor whitespace
12843   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12844         *s_ptr == ' ' || *s_ptr == '\t'))
12845     return ANIM_EVENT_NONE;
12846
12847   return result;
12848 }
12849
12850 static int get_anim_parameter_values(char *s)
12851 {
12852   int list_pos = ANIM_EVENT_UNDEFINED;
12853   int event_value = ANIM_EVENT_DEFAULT;
12854
12855   if (string_has_parameter(s, "any"))
12856     event_value |= ANIM_EVENT_ANY;
12857
12858   if (string_has_parameter(s, "click:self") ||
12859       string_has_parameter(s, "click") ||
12860       string_has_parameter(s, "self"))
12861     event_value |= ANIM_EVENT_SELF;
12862
12863   if (string_has_parameter(s, "unclick:any"))
12864     event_value |= ANIM_EVENT_UNCLICK_ANY;
12865
12866   // if animation event found, add it to global animation event list
12867   if (event_value != ANIM_EVENT_NONE)
12868     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12869
12870   while (s != NULL)
12871   {
12872     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12873     event_value = get_anim_parameter_value(s);
12874
12875     // if animation event found, add it to global animation event list
12876     if (event_value != ANIM_EVENT_NONE)
12877       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12878
12879     // continue with next part of the string, starting with next comma
12880     s = strchr(s + 1, ',');
12881   }
12882
12883   return list_pos;
12884 }
12885
12886 static int get_anim_action_parameter_value(char *token)
12887 {
12888   // check most common default case first to massively speed things up
12889   if (strEqual(token, ARG_UNDEFINED))
12890     return ANIM_EVENT_ACTION_NONE;
12891
12892   int result = getImageIDFromToken(token);
12893
12894   if (result == -1)
12895   {
12896     char *gfx_token = getStringCat2("gfx.", token);
12897
12898     result = getImageIDFromToken(gfx_token);
12899
12900     checked_free(gfx_token);
12901   }
12902
12903   if (result == -1)
12904   {
12905     Key key = getKeyFromX11KeyName(token);
12906
12907     if (key != KSYM_UNDEFINED)
12908       result = -(int)key;
12909   }
12910
12911   if (result == -1)
12912   {
12913     if (isURL(token))
12914     {
12915       result = get_hash_from_string(token);     // unsigned int => int
12916       result = ABS(result);                     // may be negative now
12917       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12918
12919       setHashEntry(anim_url_hash, int2str(result, 0), token);
12920     }
12921   }
12922
12923   if (result == -1)
12924     result = ANIM_EVENT_ACTION_NONE;
12925
12926   return result;
12927 }
12928
12929 int get_parameter_value(char *value_raw, char *suffix, int type)
12930 {
12931   char *value = getStringToLower(value_raw);
12932   int result = 0;       // probably a save default value
12933
12934   if (strEqual(suffix, ".direction"))
12935   {
12936     result = (strEqual(value, "left")  ? MV_LEFT :
12937               strEqual(value, "right") ? MV_RIGHT :
12938               strEqual(value, "up")    ? MV_UP :
12939               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12940   }
12941   else if (strEqual(suffix, ".position"))
12942   {
12943     result = (strEqual(value, "left")   ? POS_LEFT :
12944               strEqual(value, "right")  ? POS_RIGHT :
12945               strEqual(value, "top")    ? POS_TOP :
12946               strEqual(value, "upper")  ? POS_UPPER :
12947               strEqual(value, "middle") ? POS_MIDDLE :
12948               strEqual(value, "lower")  ? POS_LOWER :
12949               strEqual(value, "bottom") ? POS_BOTTOM :
12950               strEqual(value, "any")    ? POS_ANY :
12951               strEqual(value, "ce")     ? POS_CE :
12952               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12953               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12954   }
12955   else if (strEqual(suffix, ".align"))
12956   {
12957     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12958               strEqual(value, "right")  ? ALIGN_RIGHT :
12959               strEqual(value, "center") ? ALIGN_CENTER :
12960               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12961   }
12962   else if (strEqual(suffix, ".valign"))
12963   {
12964     result = (strEqual(value, "top")    ? VALIGN_TOP :
12965               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12966               strEqual(value, "middle") ? VALIGN_MIDDLE :
12967               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12968   }
12969   else if (strEqual(suffix, ".anim_mode"))
12970   {
12971     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12972               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12973               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12974               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12975               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12976               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12977               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12978               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12979               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12980               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12981               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12982               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12983               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12984               string_has_parameter(value, "all")        ? ANIM_ALL :
12985               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12986               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12987               ANIM_DEFAULT);
12988
12989     if (string_has_parameter(value, "once"))
12990       result |= ANIM_ONCE;
12991
12992     if (string_has_parameter(value, "reverse"))
12993       result |= ANIM_REVERSE;
12994
12995     if (string_has_parameter(value, "opaque_player"))
12996       result |= ANIM_OPAQUE_PLAYER;
12997
12998     if (string_has_parameter(value, "static_panel"))
12999       result |= ANIM_STATIC_PANEL;
13000   }
13001   else if (strEqual(suffix, ".init_event") ||
13002            strEqual(suffix, ".anim_event"))
13003   {
13004     result = get_anim_parameter_values(value);
13005   }
13006   else if (strEqual(suffix, ".init_delay_action") ||
13007            strEqual(suffix, ".anim_delay_action") ||
13008            strEqual(suffix, ".post_delay_action") ||
13009            strEqual(suffix, ".init_event_action") ||
13010            strEqual(suffix, ".anim_event_action"))
13011   {
13012     result = get_anim_action_parameter_value(value_raw);
13013   }
13014   else if (strEqual(suffix, ".class"))
13015   {
13016     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13017               get_hash_from_string(value));
13018   }
13019   else if (strEqual(suffix, ".style"))
13020   {
13021     result = STYLE_DEFAULT;
13022
13023     if (string_has_parameter(value, "accurate_borders"))
13024       result |= STYLE_ACCURATE_BORDERS;
13025
13026     if (string_has_parameter(value, "inner_corners"))
13027       result |= STYLE_INNER_CORNERS;
13028
13029     if (string_has_parameter(value, "reverse"))
13030       result |= STYLE_REVERSE;
13031
13032     if (string_has_parameter(value, "leftmost_position"))
13033       result |= STYLE_LEFTMOST_POSITION;
13034
13035     if (string_has_parameter(value, "block_clicks"))
13036       result |= STYLE_BLOCK;
13037
13038     if (string_has_parameter(value, "passthrough_clicks"))
13039       result |= STYLE_PASSTHROUGH;
13040
13041     if (string_has_parameter(value, "multiple_actions"))
13042       result |= STYLE_MULTIPLE_ACTIONS;
13043
13044     if (string_has_parameter(value, "consume_ce_event"))
13045       result |= STYLE_CONSUME_CE_EVENT;
13046   }
13047   else if (strEqual(suffix, ".fade_mode"))
13048   {
13049     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13050               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13051               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13052               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13053               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13054               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13055               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13056               FADE_MODE_DEFAULT);
13057   }
13058   else if (strEqual(suffix, ".auto_delay_unit"))
13059   {
13060     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13061               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13062               AUTO_DELAY_UNIT_DEFAULT);
13063   }
13064   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13065   {
13066     result = gfx.get_font_from_token_function(value);
13067   }
13068   else          // generic parameter of type integer or boolean
13069   {
13070     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13071               type == TYPE_INTEGER ? get_integer_from_string(value) :
13072               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13073               ARG_UNDEFINED_VALUE);
13074   }
13075
13076   free(value);
13077
13078   return result;
13079 }
13080
13081 static int get_token_parameter_value(char *token, char *value_raw)
13082 {
13083   char *suffix;
13084
13085   if (token == NULL || value_raw == NULL)
13086     return ARG_UNDEFINED_VALUE;
13087
13088   suffix = strrchr(token, '.');
13089   if (suffix == NULL)
13090     suffix = token;
13091
13092   if (strEqual(suffix, ".element"))
13093     return getElementFromToken(value_raw);
13094
13095   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13096   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13097 }
13098
13099 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13100                                      boolean ignore_defaults)
13101 {
13102   int i;
13103
13104   for (i = 0; image_config_vars[i].token != NULL; i++)
13105   {
13106     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13107
13108     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13109     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13110       continue;
13111
13112     if (value != NULL)
13113       *image_config_vars[i].value =
13114         get_token_parameter_value(image_config_vars[i].token, value);
13115   }
13116 }
13117
13118 void InitMenuDesignSettings_Static(void)
13119 {
13120   // always start with reliable default values from static default config
13121   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13122 }
13123
13124 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13125 {
13126   int i;
13127
13128   // the following initializes hierarchical values from static configuration
13129
13130   // special case: initialize "ARG_DEFAULT" values in static default config
13131   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13132   titlescreen_initial_first_default.fade_mode  =
13133     title_initial_first_default.fade_mode;
13134   titlescreen_initial_first_default.fade_delay =
13135     title_initial_first_default.fade_delay;
13136   titlescreen_initial_first_default.post_delay =
13137     title_initial_first_default.post_delay;
13138   titlescreen_initial_first_default.auto_delay =
13139     title_initial_first_default.auto_delay;
13140   titlescreen_initial_first_default.auto_delay_unit =
13141     title_initial_first_default.auto_delay_unit;
13142   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13143   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13144   titlescreen_first_default.post_delay = title_first_default.post_delay;
13145   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13146   titlescreen_first_default.auto_delay_unit =
13147     title_first_default.auto_delay_unit;
13148   titlemessage_initial_first_default.fade_mode  =
13149     title_initial_first_default.fade_mode;
13150   titlemessage_initial_first_default.fade_delay =
13151     title_initial_first_default.fade_delay;
13152   titlemessage_initial_first_default.post_delay =
13153     title_initial_first_default.post_delay;
13154   titlemessage_initial_first_default.auto_delay =
13155     title_initial_first_default.auto_delay;
13156   titlemessage_initial_first_default.auto_delay_unit =
13157     title_initial_first_default.auto_delay_unit;
13158   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13159   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13160   titlemessage_first_default.post_delay = title_first_default.post_delay;
13161   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13162   titlemessage_first_default.auto_delay_unit =
13163     title_first_default.auto_delay_unit;
13164
13165   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13166   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13167   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13168   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13169   titlescreen_initial_default.auto_delay_unit =
13170     title_initial_default.auto_delay_unit;
13171   titlescreen_default.fade_mode  = title_default.fade_mode;
13172   titlescreen_default.fade_delay = title_default.fade_delay;
13173   titlescreen_default.post_delay = title_default.post_delay;
13174   titlescreen_default.auto_delay = title_default.auto_delay;
13175   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13176   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13177   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13178   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13179   titlemessage_initial_default.auto_delay_unit =
13180     title_initial_default.auto_delay_unit;
13181   titlemessage_default.fade_mode  = title_default.fade_mode;
13182   titlemessage_default.fade_delay = title_default.fade_delay;
13183   titlemessage_default.post_delay = title_default.post_delay;
13184   titlemessage_default.auto_delay = title_default.auto_delay;
13185   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13186
13187   // special case: initialize "ARG_DEFAULT" values in static default config
13188   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13189   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13190   {
13191     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13192     titlescreen_first[i] = titlescreen_first_default;
13193     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13194     titlemessage_first[i] = titlemessage_first_default;
13195
13196     titlescreen_initial[i] = titlescreen_initial_default;
13197     titlescreen[i] = titlescreen_default;
13198     titlemessage_initial[i] = titlemessage_initial_default;
13199     titlemessage[i] = titlemessage_default;
13200   }
13201
13202   // special case: initialize "ARG_DEFAULT" values in static default config
13203   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13204   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13205   {
13206     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13207       continue;
13208
13209     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13210     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13211     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13212   }
13213
13214   // special case: initialize "ARG_DEFAULT" values in static default config
13215   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13216   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13217   {
13218     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13219     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13220     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13221
13222     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13223       continue;
13224
13225     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13226   }
13227 }
13228
13229 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13230 {
13231   static struct
13232   {
13233     struct XY *dst, *src;
13234   }
13235   game_buttons_xy[] =
13236   {
13237     { &game.button.save,        &game.button.stop       },
13238     { &game.button.pause2,      &game.button.pause      },
13239     { &game.button.load,        &game.button.play       },
13240     { &game.button.undo,        &game.button.stop       },
13241     { &game.button.redo,        &game.button.play       },
13242
13243     { NULL,                     NULL                    }
13244   };
13245   int i, j;
13246
13247   // special case: initialize later added SETUP list size from LEVELS value
13248   if (menu.list_size[GAME_MODE_SETUP] == -1)
13249     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13250
13251   // set default position for snapshot buttons to stop/pause/play buttons
13252   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13253     if ((*game_buttons_xy[i].dst).x == -1 &&
13254         (*game_buttons_xy[i].dst).y == -1)
13255       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13256
13257   // --------------------------------------------------------------------------
13258   // dynamic viewports (including playfield margins, borders and alignments)
13259   // --------------------------------------------------------------------------
13260
13261   // dynamic viewports currently only supported for landscape mode
13262   int display_width  = MAX(video.display_width, video.display_height);
13263   int display_height = MIN(video.display_width, video.display_height);
13264
13265   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13266   {
13267     struct RectWithBorder *vp_window    = &viewport.window[i];
13268     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13269     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13270     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13271     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13272     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13273     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13274     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13275
13276     // adjust window size if min/max width/height is specified
13277
13278     if (vp_window->min_width != -1)
13279     {
13280       int window_width = display_width;
13281
13282       // when using static window height, use aspect ratio of display
13283       if (vp_window->min_height == -1)
13284         window_width = vp_window->height * display_width / display_height;
13285
13286       vp_window->width = MAX(vp_window->min_width, window_width);
13287     }
13288
13289     if (vp_window->min_height != -1)
13290     {
13291       int window_height = display_height;
13292
13293       // when using static window width, use aspect ratio of display
13294       if (vp_window->min_width == -1)
13295         window_height = vp_window->width * display_height / display_width;
13296
13297       vp_window->height = MAX(vp_window->min_height, window_height);
13298     }
13299
13300     if (vp_window->max_width != -1)
13301       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13302
13303     if (vp_window->max_height != -1)
13304       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13305
13306     int playfield_width  = vp_window->width;
13307     int playfield_height = vp_window->height;
13308
13309     // adjust playfield size and position according to specified margins
13310
13311     playfield_width  -= vp_playfield->margin_left;
13312     playfield_width  -= vp_playfield->margin_right;
13313
13314     playfield_height -= vp_playfield->margin_top;
13315     playfield_height -= vp_playfield->margin_bottom;
13316
13317     // adjust playfield size if min/max width/height is specified
13318
13319     if (vp_playfield->min_width != -1)
13320       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13321
13322     if (vp_playfield->min_height != -1)
13323       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13324
13325     if (vp_playfield->max_width != -1)
13326       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13327
13328     if (vp_playfield->max_height != -1)
13329       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13330
13331     // adjust playfield position according to specified alignment
13332
13333     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13334       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13335     else if (vp_playfield->align == ALIGN_CENTER)
13336       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13337     else if (vp_playfield->align == ALIGN_RIGHT)
13338       vp_playfield->x += playfield_width - vp_playfield->width;
13339
13340     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13341       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13342     else if (vp_playfield->valign == VALIGN_MIDDLE)
13343       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13344     else if (vp_playfield->valign == VALIGN_BOTTOM)
13345       vp_playfield->y += playfield_height - vp_playfield->height;
13346
13347     vp_playfield->x += vp_playfield->margin_left;
13348     vp_playfield->y += vp_playfield->margin_top;
13349
13350     // adjust individual playfield borders if only default border is specified
13351
13352     if (vp_playfield->border_left == -1)
13353       vp_playfield->border_left = vp_playfield->border_size;
13354     if (vp_playfield->border_right == -1)
13355       vp_playfield->border_right = vp_playfield->border_size;
13356     if (vp_playfield->border_top == -1)
13357       vp_playfield->border_top = vp_playfield->border_size;
13358     if (vp_playfield->border_bottom == -1)
13359       vp_playfield->border_bottom = vp_playfield->border_size;
13360
13361     // set dynamic playfield borders if borders are specified as undefined
13362     // (but only if window size was dynamic and playfield size was static)
13363
13364     if (dynamic_window_width && !dynamic_playfield_width)
13365     {
13366       if (vp_playfield->border_left == -1)
13367       {
13368         vp_playfield->border_left = (vp_playfield->x -
13369                                      vp_playfield->margin_left);
13370         vp_playfield->x     -= vp_playfield->border_left;
13371         vp_playfield->width += vp_playfield->border_left;
13372       }
13373
13374       if (vp_playfield->border_right == -1)
13375       {
13376         vp_playfield->border_right = (vp_window->width -
13377                                       vp_playfield->x -
13378                                       vp_playfield->width -
13379                                       vp_playfield->margin_right);
13380         vp_playfield->width += vp_playfield->border_right;
13381       }
13382     }
13383
13384     if (dynamic_window_height && !dynamic_playfield_height)
13385     {
13386       if (vp_playfield->border_top == -1)
13387       {
13388         vp_playfield->border_top = (vp_playfield->y -
13389                                     vp_playfield->margin_top);
13390         vp_playfield->y      -= vp_playfield->border_top;
13391         vp_playfield->height += vp_playfield->border_top;
13392       }
13393
13394       if (vp_playfield->border_bottom == -1)
13395       {
13396         vp_playfield->border_bottom = (vp_window->height -
13397                                        vp_playfield->y -
13398                                        vp_playfield->height -
13399                                        vp_playfield->margin_bottom);
13400         vp_playfield->height += vp_playfield->border_bottom;
13401       }
13402     }
13403
13404     // adjust playfield size to be a multiple of a defined alignment tile size
13405
13406     int align_size = vp_playfield->align_size;
13407     int playfield_xtiles = vp_playfield->width  / align_size;
13408     int playfield_ytiles = vp_playfield->height / align_size;
13409     int playfield_width_corrected  = playfield_xtiles * align_size;
13410     int playfield_height_corrected = playfield_ytiles * align_size;
13411     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13412                                  i == GFX_SPECIAL_ARG_EDITOR);
13413
13414     if (is_playfield_mode &&
13415         dynamic_playfield_width &&
13416         vp_playfield->width != playfield_width_corrected)
13417     {
13418       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13419
13420       vp_playfield->width = playfield_width_corrected;
13421
13422       if (vp_playfield->align == ALIGN_LEFT)
13423       {
13424         vp_playfield->border_left += playfield_xdiff;
13425       }
13426       else if (vp_playfield->align == ALIGN_RIGHT)
13427       {
13428         vp_playfield->border_right += playfield_xdiff;
13429       }
13430       else if (vp_playfield->align == ALIGN_CENTER)
13431       {
13432         int border_left_diff  = playfield_xdiff / 2;
13433         int border_right_diff = playfield_xdiff - border_left_diff;
13434
13435         vp_playfield->border_left  += border_left_diff;
13436         vp_playfield->border_right += border_right_diff;
13437       }
13438     }
13439
13440     if (is_playfield_mode &&
13441         dynamic_playfield_height &&
13442         vp_playfield->height != playfield_height_corrected)
13443     {
13444       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13445
13446       vp_playfield->height = playfield_height_corrected;
13447
13448       if (vp_playfield->valign == VALIGN_TOP)
13449       {
13450         vp_playfield->border_top += playfield_ydiff;
13451       }
13452       else if (vp_playfield->align == VALIGN_BOTTOM)
13453       {
13454         vp_playfield->border_right += playfield_ydiff;
13455       }
13456       else if (vp_playfield->align == VALIGN_MIDDLE)
13457       {
13458         int border_top_diff    = playfield_ydiff / 2;
13459         int border_bottom_diff = playfield_ydiff - border_top_diff;
13460
13461         vp_playfield->border_top    += border_top_diff;
13462         vp_playfield->border_bottom += border_bottom_diff;
13463       }
13464     }
13465
13466     // adjust door positions according to specified alignment
13467
13468     for (j = 0; j < 2; j++)
13469     {
13470       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13471
13472       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13473         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13474       else if (vp_door->align == ALIGN_CENTER)
13475         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13476       else if (vp_door->align == ALIGN_RIGHT)
13477         vp_door->x += vp_window->width - vp_door->width;
13478
13479       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13480         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13481       else if (vp_door->valign == VALIGN_MIDDLE)
13482         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13483       else if (vp_door->valign == VALIGN_BOTTOM)
13484         vp_door->y += vp_window->height - vp_door->height;
13485     }
13486   }
13487 }
13488
13489 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13490 {
13491   static struct
13492   {
13493     struct XYTileSize *dst, *src;
13494     int graphic;
13495   }
13496   editor_buttons_xy[] =
13497   {
13498     {
13499       &editor.button.element_left,      &editor.palette.element_left,
13500       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13501     },
13502     {
13503       &editor.button.element_middle,    &editor.palette.element_middle,
13504       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13505     },
13506     {
13507       &editor.button.element_right,     &editor.palette.element_right,
13508       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13509     },
13510
13511     { NULL,                     NULL                    }
13512   };
13513   int i;
13514
13515   // set default position for element buttons to element graphics
13516   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13517   {
13518     if ((*editor_buttons_xy[i].dst).x == -1 &&
13519         (*editor_buttons_xy[i].dst).y == -1)
13520     {
13521       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13522
13523       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13524
13525       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13526     }
13527   }
13528
13529   // adjust editor palette rows and columns if specified to be dynamic
13530
13531   if (editor.palette.cols == -1)
13532   {
13533     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13534     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13535     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13536
13537     editor.palette.cols = (vp_width - sc_width) / bt_width;
13538
13539     if (editor.palette.x == -1)
13540     {
13541       int palette_width = editor.palette.cols * bt_width + sc_width;
13542
13543       editor.palette.x = (vp_width - palette_width) / 2;
13544     }
13545   }
13546
13547   if (editor.palette.rows == -1)
13548   {
13549     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13550     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13551     int tx_height = getFontHeight(FONT_TEXT_2);
13552
13553     editor.palette.rows = (vp_height - tx_height) / bt_height;
13554
13555     if (editor.palette.y == -1)
13556     {
13557       int palette_height = editor.palette.rows * bt_height + tx_height;
13558
13559       editor.palette.y = (vp_height - palette_height) / 2;
13560     }
13561   }
13562 }
13563
13564 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13565                                                       boolean initialize)
13566 {
13567   // special case: check if network and preview player positions are redefined,
13568   // to compare this later against the main menu level preview being redefined
13569   struct TokenIntPtrInfo menu_config_players[] =
13570   {
13571     { "main.network_players.x", &menu.main.network_players.redefined    },
13572     { "main.network_players.y", &menu.main.network_players.redefined    },
13573     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13574     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13575     { "preview.x",              &preview.redefined                      },
13576     { "preview.y",              &preview.redefined                      }
13577   };
13578   int i;
13579
13580   if (initialize)
13581   {
13582     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13583       *menu_config_players[i].value = FALSE;
13584   }
13585   else
13586   {
13587     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13588       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13589         *menu_config_players[i].value = TRUE;
13590   }
13591 }
13592
13593 static void InitMenuDesignSettings_PreviewPlayers(void)
13594 {
13595   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13596 }
13597
13598 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13599 {
13600   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13601 }
13602
13603 static void LoadMenuDesignSettingsFromFilename(char *filename)
13604 {
13605   static struct TitleFadingInfo tfi;
13606   static struct TitleMessageInfo tmi;
13607   static struct TokenInfo title_tokens[] =
13608   {
13609     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13610     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13611     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13612     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13613     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13614
13615     { -1,               NULL,                   NULL                    }
13616   };
13617   static struct TokenInfo titlemessage_tokens[] =
13618   {
13619     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13620     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13621     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13622     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13623     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13624     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13625     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13626     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13627     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13628     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13629     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13630     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13631     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13632     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13633     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13634     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13635     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13636     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13637
13638     { -1,               NULL,                   NULL                    }
13639   };
13640   static struct
13641   {
13642     struct TitleFadingInfo *info;
13643     char *text;
13644   }
13645   title_info[] =
13646   {
13647     // initialize first titles from "enter screen" definitions, if defined
13648     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13649     { &title_first_default,             "menu.enter_screen.TITLE"       },
13650
13651     // initialize title screens from "next screen" definitions, if defined
13652     { &title_initial_default,           "menu.next_screen.TITLE"        },
13653     { &title_default,                   "menu.next_screen.TITLE"        },
13654
13655     { NULL,                             NULL                            }
13656   };
13657   static struct
13658   {
13659     struct TitleMessageInfo *array;
13660     char *text;
13661   }
13662   titlemessage_arrays[] =
13663   {
13664     // initialize first titles from "enter screen" definitions, if defined
13665     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13666     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13667     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13668     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13669
13670     // initialize titles from "next screen" definitions, if defined
13671     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13672     { titlescreen,                      "menu.next_screen.TITLE"        },
13673     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13674     { titlemessage,                     "menu.next_screen.TITLE"        },
13675
13676     // overwrite titles with title definitions, if defined
13677     { titlescreen_initial_first,        "[title_initial]"               },
13678     { titlescreen_first,                "[title]"                       },
13679     { titlemessage_initial_first,       "[title_initial]"               },
13680     { titlemessage_first,               "[title]"                       },
13681
13682     { titlescreen_initial,              "[title_initial]"               },
13683     { titlescreen,                      "[title]"                       },
13684     { titlemessage_initial,             "[title_initial]"               },
13685     { titlemessage,                     "[title]"                       },
13686
13687     // overwrite titles with title screen/message definitions, if defined
13688     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13689     { titlescreen_first,                "[titlescreen]"                 },
13690     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13691     { titlemessage_first,               "[titlemessage]"                },
13692
13693     { titlescreen_initial,              "[titlescreen_initial]"         },
13694     { titlescreen,                      "[titlescreen]"                 },
13695     { titlemessage_initial,             "[titlemessage_initial]"        },
13696     { titlemessage,                     "[titlemessage]"                },
13697
13698     { NULL,                             NULL                            }
13699   };
13700   SetupFileHash *setup_file_hash;
13701   int i, j, k;
13702
13703   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13704     return;
13705
13706   // the following initializes hierarchical values from dynamic configuration
13707
13708   // special case: initialize with default values that may be overwritten
13709   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13710   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13711   {
13712     struct TokenIntPtrInfo menu_config[] =
13713     {
13714       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13715       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13716       { "menu.list_size",       &menu.list_size[i]      }
13717     };
13718
13719     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13720     {
13721       char *token = menu_config[j].token;
13722       char *value = getHashEntry(setup_file_hash, token);
13723
13724       if (value != NULL)
13725         *menu_config[j].value = get_integer_from_string(value);
13726     }
13727   }
13728
13729   // special case: initialize with default values that may be overwritten
13730   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13731   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13732   {
13733     struct TokenIntPtrInfo menu_config[] =
13734     {
13735       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13736       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13737       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13738       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13739       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13740     };
13741
13742     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13743     {
13744       char *token = menu_config[j].token;
13745       char *value = getHashEntry(setup_file_hash, token);
13746
13747       if (value != NULL)
13748         *menu_config[j].value = get_integer_from_string(value);
13749     }
13750   }
13751
13752   // special case: initialize with default values that may be overwritten
13753   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13754   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13755   {
13756     struct TokenIntPtrInfo menu_config[] =
13757     {
13758       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13759       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13760     };
13761
13762     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13763     {
13764       char *token = menu_config[j].token;
13765       char *value = getHashEntry(setup_file_hash, token);
13766
13767       if (value != NULL)
13768         *menu_config[j].value = get_integer_from_string(value);
13769     }
13770   }
13771
13772   // special case: initialize with default values that may be overwritten
13773   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13774   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13775   {
13776     struct TokenIntPtrInfo menu_config[] =
13777     {
13778       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13779       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13780       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13781       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13782       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13783       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13784       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13785       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13786       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13787       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13788     };
13789
13790     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13791     {
13792       char *token = menu_config[j].token;
13793       char *value = getHashEntry(setup_file_hash, token);
13794
13795       if (value != NULL)
13796         *menu_config[j].value = get_integer_from_string(value);
13797     }
13798   }
13799
13800   // special case: initialize with default values that may be overwritten
13801   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13802   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13803   {
13804     struct TokenIntPtrInfo menu_config[] =
13805     {
13806       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13807       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13808       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13809       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13810       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13811       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13812       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13813       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13814       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13815     };
13816
13817     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13818     {
13819       char *token = menu_config[j].token;
13820       char *value = getHashEntry(setup_file_hash, token);
13821
13822       if (value != NULL)
13823         *menu_config[j].value = get_token_parameter_value(token, value);
13824     }
13825   }
13826
13827   // special case: initialize with default values that may be overwritten
13828   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13829   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13830   {
13831     struct
13832     {
13833       char *token_prefix;
13834       struct RectWithBorder *struct_ptr;
13835     }
13836     vp_struct[] =
13837     {
13838       { "viewport.window",      &viewport.window[i]     },
13839       { "viewport.playfield",   &viewport.playfield[i]  },
13840       { "viewport.door_1",      &viewport.door_1[i]     },
13841       { "viewport.door_2",      &viewport.door_2[i]     }
13842     };
13843
13844     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13845     {
13846       struct TokenIntPtrInfo vp_config[] =
13847       {
13848         { ".x",                 &vp_struct[j].struct_ptr->x             },
13849         { ".y",                 &vp_struct[j].struct_ptr->y             },
13850         { ".width",             &vp_struct[j].struct_ptr->width         },
13851         { ".height",            &vp_struct[j].struct_ptr->height        },
13852         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13853         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13854         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13855         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13856         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13857         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13858         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13859         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13860         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13861         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13862         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13863         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13864         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13865         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13866         { ".align",             &vp_struct[j].struct_ptr->align         },
13867         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13868       };
13869
13870       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13871       {
13872         char *token = getStringCat2(vp_struct[j].token_prefix,
13873                                     vp_config[k].token);
13874         char *value = getHashEntry(setup_file_hash, token);
13875
13876         if (value != NULL)
13877           *vp_config[k].value = get_token_parameter_value(token, value);
13878
13879         free(token);
13880       }
13881     }
13882   }
13883
13884   // special case: initialize with default values that may be overwritten
13885   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13886   for (i = 0; title_info[i].info != NULL; i++)
13887   {
13888     struct TitleFadingInfo *info = title_info[i].info;
13889     char *base_token = title_info[i].text;
13890
13891     for (j = 0; title_tokens[j].type != -1; j++)
13892     {
13893       char *token = getStringCat2(base_token, title_tokens[j].text);
13894       char *value = getHashEntry(setup_file_hash, token);
13895
13896       if (value != NULL)
13897       {
13898         int parameter_value = get_token_parameter_value(token, value);
13899
13900         tfi = *info;
13901
13902         *(int *)title_tokens[j].value = (int)parameter_value;
13903
13904         *info = tfi;
13905       }
13906
13907       free(token);
13908     }
13909   }
13910
13911   // special case: initialize with default values that may be overwritten
13912   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13913   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13914   {
13915     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13916     char *base_token = titlemessage_arrays[i].text;
13917
13918     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13919     {
13920       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13921       char *value = getHashEntry(setup_file_hash, token);
13922
13923       if (value != NULL)
13924       {
13925         int parameter_value = get_token_parameter_value(token, value);
13926
13927         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13928         {
13929           tmi = array[k];
13930
13931           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13932             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13933           else
13934             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13935
13936           array[k] = tmi;
13937         }
13938       }
13939
13940       free(token);
13941     }
13942   }
13943
13944   // read (and overwrite with) values that may be specified in config file
13945   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13946
13947   // special case: check if network and preview player positions are redefined
13948   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13949
13950   freeSetupFileHash(setup_file_hash);
13951 }
13952
13953 void LoadMenuDesignSettings(void)
13954 {
13955   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13956
13957   InitMenuDesignSettings_Static();
13958   InitMenuDesignSettings_SpecialPreProcessing();
13959   InitMenuDesignSettings_PreviewPlayers();
13960
13961   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13962   {
13963     // first look for special settings configured in level series config
13964     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13965
13966     if (fileExists(filename_base))
13967       LoadMenuDesignSettingsFromFilename(filename_base);
13968   }
13969
13970   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13971
13972   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13973     LoadMenuDesignSettingsFromFilename(filename_local);
13974
13975   InitMenuDesignSettings_SpecialPostProcessing();
13976 }
13977
13978 void LoadMenuDesignSettings_AfterGraphics(void)
13979 {
13980   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13981 }
13982
13983 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13984                                 boolean ignore_defaults)
13985 {
13986   int i;
13987
13988   for (i = 0; sound_config_vars[i].token != NULL; i++)
13989   {
13990     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13991
13992     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13993     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13994       continue;
13995
13996     if (value != NULL)
13997       *sound_config_vars[i].value =
13998         get_token_parameter_value(sound_config_vars[i].token, value);
13999   }
14000 }
14001
14002 void InitSoundSettings_Static(void)
14003 {
14004   // always start with reliable default values from static default config
14005   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14006 }
14007
14008 static void LoadSoundSettingsFromFilename(char *filename)
14009 {
14010   SetupFileHash *setup_file_hash;
14011
14012   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14013     return;
14014
14015   // read (and overwrite with) values that may be specified in config file
14016   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14017
14018   freeSetupFileHash(setup_file_hash);
14019 }
14020
14021 void LoadSoundSettings(void)
14022 {
14023   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14024
14025   InitSoundSettings_Static();
14026
14027   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14028   {
14029     // first look for special settings configured in level series config
14030     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14031
14032     if (fileExists(filename_base))
14033       LoadSoundSettingsFromFilename(filename_base);
14034   }
14035
14036   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14037
14038   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14039     LoadSoundSettingsFromFilename(filename_local);
14040 }
14041
14042 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14043 {
14044   char *filename = getEditorSetupFilename();
14045   SetupFileList *setup_file_list, *list;
14046   SetupFileHash *element_hash;
14047   int num_unknown_tokens = 0;
14048   int i;
14049
14050   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14051     return;
14052
14053   element_hash = newSetupFileHash();
14054
14055   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14056     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14057
14058   // determined size may be larger than needed (due to unknown elements)
14059   *num_elements = 0;
14060   for (list = setup_file_list; list != NULL; list = list->next)
14061     (*num_elements)++;
14062
14063   // add space for up to 3 more elements for padding that may be needed
14064   *num_elements += 3;
14065
14066   // free memory for old list of elements, if needed
14067   checked_free(*elements);
14068
14069   // allocate memory for new list of elements
14070   *elements = checked_malloc(*num_elements * sizeof(int));
14071
14072   *num_elements = 0;
14073   for (list = setup_file_list; list != NULL; list = list->next)
14074   {
14075     char *value = getHashEntry(element_hash, list->token);
14076
14077     if (value == NULL)          // try to find obsolete token mapping
14078     {
14079       char *mapped_token = get_mapped_token(list->token);
14080
14081       if (mapped_token != NULL)
14082       {
14083         value = getHashEntry(element_hash, mapped_token);
14084
14085         free(mapped_token);
14086       }
14087     }
14088
14089     if (value != NULL)
14090     {
14091       (*elements)[(*num_elements)++] = atoi(value);
14092     }
14093     else
14094     {
14095       if (num_unknown_tokens == 0)
14096       {
14097         Warn("---");
14098         Warn("unknown token(s) found in config file:");
14099         Warn("- config file: '%s'", filename);
14100
14101         num_unknown_tokens++;
14102       }
14103
14104       Warn("- token: '%s'", list->token);
14105     }
14106   }
14107
14108   if (num_unknown_tokens > 0)
14109     Warn("---");
14110
14111   while (*num_elements % 4)     // pad with empty elements, if needed
14112     (*elements)[(*num_elements)++] = EL_EMPTY;
14113
14114   freeSetupFileList(setup_file_list);
14115   freeSetupFileHash(element_hash);
14116
14117 #if 0
14118   for (i = 0; i < *num_elements; i++)
14119     Debug("editor", "element '%s' [%d]\n",
14120           element_info[(*elements)[i]].token_name, (*elements)[i]);
14121 #endif
14122 }
14123
14124 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14125                                                      boolean is_sound)
14126 {
14127   SetupFileHash *setup_file_hash = NULL;
14128   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14129   char *filename_music, *filename_prefix, *filename_info;
14130   struct
14131   {
14132     char *token;
14133     char **value_ptr;
14134   }
14135   token_to_value_ptr[] =
14136   {
14137     { "title_header",   &tmp_music_file_info.title_header       },
14138     { "artist_header",  &tmp_music_file_info.artist_header      },
14139     { "album_header",   &tmp_music_file_info.album_header       },
14140     { "year_header",    &tmp_music_file_info.year_header        },
14141     { "played_header",  &tmp_music_file_info.played_header      },
14142
14143     { "title",          &tmp_music_file_info.title              },
14144     { "artist",         &tmp_music_file_info.artist             },
14145     { "album",          &tmp_music_file_info.album              },
14146     { "year",           &tmp_music_file_info.year               },
14147     { "played",         &tmp_music_file_info.played             },
14148
14149     { NULL,             NULL                                    },
14150   };
14151   int i;
14152
14153   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14154                     getCustomMusicFilename(basename));
14155
14156   if (filename_music == NULL)
14157     return NULL;
14158
14159   // ---------- try to replace file extension ----------
14160
14161   filename_prefix = getStringCopy(filename_music);
14162   if (strrchr(filename_prefix, '.') != NULL)
14163     *strrchr(filename_prefix, '.') = '\0';
14164   filename_info = getStringCat2(filename_prefix, ".txt");
14165
14166   if (fileExists(filename_info))
14167     setup_file_hash = loadSetupFileHash(filename_info);
14168
14169   free(filename_prefix);
14170   free(filename_info);
14171
14172   if (setup_file_hash == NULL)
14173   {
14174     // ---------- try to add file extension ----------
14175
14176     filename_prefix = getStringCopy(filename_music);
14177     filename_info = getStringCat2(filename_prefix, ".txt");
14178
14179     if (fileExists(filename_info))
14180       setup_file_hash = loadSetupFileHash(filename_info);
14181
14182     free(filename_prefix);
14183     free(filename_info);
14184   }
14185
14186   if (setup_file_hash == NULL)
14187     return NULL;
14188
14189   // ---------- music file info found ----------
14190
14191   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14192
14193   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14194   {
14195     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14196
14197     *token_to_value_ptr[i].value_ptr =
14198       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14199   }
14200
14201   tmp_music_file_info.basename = getStringCopy(basename);
14202   tmp_music_file_info.music = music;
14203   tmp_music_file_info.is_sound = is_sound;
14204
14205   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14206   *new_music_file_info = tmp_music_file_info;
14207
14208   return new_music_file_info;
14209 }
14210
14211 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14212 {
14213   return get_music_file_info_ext(basename, music, FALSE);
14214 }
14215
14216 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14217 {
14218   return get_music_file_info_ext(basename, sound, TRUE);
14219 }
14220
14221 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14222                                      char *basename, boolean is_sound)
14223 {
14224   for (; list != NULL; list = list->next)
14225     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14226       return TRUE;
14227
14228   return FALSE;
14229 }
14230
14231 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14232 {
14233   return music_info_listed_ext(list, basename, FALSE);
14234 }
14235
14236 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14237 {
14238   return music_info_listed_ext(list, basename, TRUE);
14239 }
14240
14241 void LoadMusicInfo(void)
14242 {
14243   int num_music_noconf = getMusicListSize_NoConf();
14244   int num_music = getMusicListSize();
14245   int num_sounds = getSoundListSize();
14246   struct FileInfo *music, *sound;
14247   struct MusicFileInfo *next, **new;
14248
14249   int i;
14250
14251   while (music_file_info != NULL)
14252   {
14253     next = music_file_info->next;
14254
14255     checked_free(music_file_info->basename);
14256
14257     checked_free(music_file_info->title_header);
14258     checked_free(music_file_info->artist_header);
14259     checked_free(music_file_info->album_header);
14260     checked_free(music_file_info->year_header);
14261     checked_free(music_file_info->played_header);
14262
14263     checked_free(music_file_info->title);
14264     checked_free(music_file_info->artist);
14265     checked_free(music_file_info->album);
14266     checked_free(music_file_info->year);
14267     checked_free(music_file_info->played);
14268
14269     free(music_file_info);
14270
14271     music_file_info = next;
14272   }
14273
14274   new = &music_file_info;
14275
14276   // get (configured or unconfigured) music file info for all levels
14277   for (i = leveldir_current->first_level;
14278        i <= leveldir_current->last_level; i++)
14279   {
14280     int music_nr;
14281
14282     if (levelset.music[i] != MUS_UNDEFINED)
14283     {
14284       // get music file info for configured level music
14285       music_nr = levelset.music[i];
14286     }
14287     else if (num_music_noconf > 0)
14288     {
14289       // get music file info for unconfigured level music
14290       int level_pos = i - leveldir_current->first_level;
14291
14292       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14293     }
14294     else
14295     {
14296       continue;
14297     }
14298
14299     char *basename = getMusicInfoEntryFilename(music_nr);
14300
14301     if (basename == NULL)
14302       continue;
14303
14304     if (!music_info_listed(music_file_info, basename))
14305     {
14306       *new = get_music_file_info(basename, music_nr);
14307
14308       if (*new != NULL)
14309         new = &(*new)->next;
14310     }
14311   }
14312
14313   // get music file info for all remaining configured music files
14314   for (i = 0; i < num_music; i++)
14315   {
14316     music = getMusicListEntry(i);
14317
14318     if (music->filename == NULL)
14319       continue;
14320
14321     if (strEqual(music->filename, UNDEFINED_FILENAME))
14322       continue;
14323
14324     // a configured file may be not recognized as music
14325     if (!FileIsMusic(music->filename))
14326       continue;
14327
14328     if (!music_info_listed(music_file_info, music->filename))
14329     {
14330       *new = get_music_file_info(music->filename, i);
14331
14332       if (*new != NULL)
14333         new = &(*new)->next;
14334     }
14335   }
14336
14337   // get sound file info for all configured sound files
14338   for (i = 0; i < num_sounds; i++)
14339   {
14340     sound = getSoundListEntry(i);
14341
14342     if (sound->filename == NULL)
14343       continue;
14344
14345     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14346       continue;
14347
14348     // a configured file may be not recognized as sound
14349     if (!FileIsSound(sound->filename))
14350       continue;
14351
14352     if (!sound_info_listed(music_file_info, sound->filename))
14353     {
14354       *new = get_sound_file_info(sound->filename, i);
14355       if (*new != NULL)
14356         new = &(*new)->next;
14357     }
14358   }
14359
14360   // add pointers to previous list nodes
14361
14362   struct MusicFileInfo *node = music_file_info;
14363
14364   while (node != NULL)
14365   {
14366     if (node->next)
14367       node->next->prev = node;
14368
14369     node = node->next;
14370   }
14371 }
14372
14373 static void add_helpanim_entry(int element, int action, int direction,
14374                                int delay, int *num_list_entries)
14375 {
14376   struct HelpAnimInfo *new_list_entry;
14377   (*num_list_entries)++;
14378
14379   helpanim_info =
14380     checked_realloc(helpanim_info,
14381                     *num_list_entries * sizeof(struct HelpAnimInfo));
14382   new_list_entry = &helpanim_info[*num_list_entries - 1];
14383
14384   new_list_entry->element = element;
14385   new_list_entry->action = action;
14386   new_list_entry->direction = direction;
14387   new_list_entry->delay = delay;
14388 }
14389
14390 static void print_unknown_token(char *filename, char *token, int token_nr)
14391 {
14392   if (token_nr == 0)
14393   {
14394     Warn("---");
14395     Warn("unknown token(s) found in config file:");
14396     Warn("- config file: '%s'", filename);
14397   }
14398
14399   Warn("- token: '%s'", token);
14400 }
14401
14402 static void print_unknown_token_end(int token_nr)
14403 {
14404   if (token_nr > 0)
14405     Warn("---");
14406 }
14407
14408 void LoadHelpAnimInfo(void)
14409 {
14410   char *filename = getHelpAnimFilename();
14411   SetupFileList *setup_file_list = NULL, *list;
14412   SetupFileHash *element_hash, *action_hash, *direction_hash;
14413   int num_list_entries = 0;
14414   int num_unknown_tokens = 0;
14415   int i;
14416
14417   if (fileExists(filename))
14418     setup_file_list = loadSetupFileList(filename);
14419
14420   if (setup_file_list == NULL)
14421   {
14422     // use reliable default values from static configuration
14423     SetupFileList *insert_ptr;
14424
14425     insert_ptr = setup_file_list =
14426       newSetupFileList(helpanim_config[0].token,
14427                        helpanim_config[0].value);
14428
14429     for (i = 1; helpanim_config[i].token; i++)
14430       insert_ptr = addListEntry(insert_ptr,
14431                                 helpanim_config[i].token,
14432                                 helpanim_config[i].value);
14433   }
14434
14435   element_hash   = newSetupFileHash();
14436   action_hash    = newSetupFileHash();
14437   direction_hash = newSetupFileHash();
14438
14439   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14440     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14441
14442   for (i = 0; i < NUM_ACTIONS; i++)
14443     setHashEntry(action_hash, element_action_info[i].suffix,
14444                  i_to_a(element_action_info[i].value));
14445
14446   // do not store direction index (bit) here, but direction value!
14447   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14448     setHashEntry(direction_hash, element_direction_info[i].suffix,
14449                  i_to_a(1 << element_direction_info[i].value));
14450
14451   for (list = setup_file_list; list != NULL; list = list->next)
14452   {
14453     char *element_token, *action_token, *direction_token;
14454     char *element_value, *action_value, *direction_value;
14455     int delay = atoi(list->value);
14456
14457     if (strEqual(list->token, "end"))
14458     {
14459       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14460
14461       continue;
14462     }
14463
14464     /* first try to break element into element/action/direction parts;
14465        if this does not work, also accept combined "element[.act][.dir]"
14466        elements (like "dynamite.active"), which are unique elements */
14467
14468     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14469     {
14470       element_value = getHashEntry(element_hash, list->token);
14471       if (element_value != NULL)        // element found
14472         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14473                            &num_list_entries);
14474       else
14475       {
14476         // no further suffixes found -- this is not an element
14477         print_unknown_token(filename, list->token, num_unknown_tokens++);
14478       }
14479
14480       continue;
14481     }
14482
14483     // token has format "<prefix>.<something>"
14484
14485     action_token = strchr(list->token, '.');    // suffix may be action ...
14486     direction_token = action_token;             // ... or direction
14487
14488     element_token = getStringCopy(list->token);
14489     *strchr(element_token, '.') = '\0';
14490
14491     element_value = getHashEntry(element_hash, element_token);
14492
14493     if (element_value == NULL)          // this is no element
14494     {
14495       element_value = getHashEntry(element_hash, list->token);
14496       if (element_value != NULL)        // combined element found
14497         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14498                            &num_list_entries);
14499       else
14500         print_unknown_token(filename, list->token, num_unknown_tokens++);
14501
14502       free(element_token);
14503
14504       continue;
14505     }
14506
14507     action_value = getHashEntry(action_hash, action_token);
14508
14509     if (action_value != NULL)           // action found
14510     {
14511       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14512                     &num_list_entries);
14513
14514       free(element_token);
14515
14516       continue;
14517     }
14518
14519     direction_value = getHashEntry(direction_hash, direction_token);
14520
14521     if (direction_value != NULL)        // direction found
14522     {
14523       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14524                          &num_list_entries);
14525
14526       free(element_token);
14527
14528       continue;
14529     }
14530
14531     if (strchr(action_token + 1, '.') == NULL)
14532     {
14533       // no further suffixes found -- this is not an action nor direction
14534
14535       element_value = getHashEntry(element_hash, list->token);
14536       if (element_value != NULL)        // combined element found
14537         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14538                            &num_list_entries);
14539       else
14540         print_unknown_token(filename, list->token, num_unknown_tokens++);
14541
14542       free(element_token);
14543
14544       continue;
14545     }
14546
14547     // token has format "<prefix>.<suffix>.<something>"
14548
14549     direction_token = strchr(action_token + 1, '.');
14550
14551     action_token = getStringCopy(action_token);
14552     *strchr(action_token + 1, '.') = '\0';
14553
14554     action_value = getHashEntry(action_hash, action_token);
14555
14556     if (action_value == NULL)           // this is no action
14557     {
14558       element_value = getHashEntry(element_hash, list->token);
14559       if (element_value != NULL)        // combined element found
14560         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14561                            &num_list_entries);
14562       else
14563         print_unknown_token(filename, list->token, num_unknown_tokens++);
14564
14565       free(element_token);
14566       free(action_token);
14567
14568       continue;
14569     }
14570
14571     direction_value = getHashEntry(direction_hash, direction_token);
14572
14573     if (direction_value != NULL)        // direction found
14574     {
14575       add_helpanim_entry(atoi(element_value), atoi(action_value),
14576                          atoi(direction_value), delay, &num_list_entries);
14577
14578       free(element_token);
14579       free(action_token);
14580
14581       continue;
14582     }
14583
14584     // this is no direction
14585
14586     element_value = getHashEntry(element_hash, list->token);
14587     if (element_value != NULL)          // combined element found
14588       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14589                          &num_list_entries);
14590     else
14591       print_unknown_token(filename, list->token, num_unknown_tokens++);
14592
14593     free(element_token);
14594     free(action_token);
14595   }
14596
14597   print_unknown_token_end(num_unknown_tokens);
14598
14599   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14600   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14601
14602   freeSetupFileList(setup_file_list);
14603   freeSetupFileHash(element_hash);
14604   freeSetupFileHash(action_hash);
14605   freeSetupFileHash(direction_hash);
14606
14607 #if 0
14608   for (i = 0; i < num_list_entries; i++)
14609     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14610           EL_NAME(helpanim_info[i].element),
14611           helpanim_info[i].element,
14612           helpanim_info[i].action,
14613           helpanim_info[i].direction,
14614           helpanim_info[i].delay);
14615 #endif
14616 }
14617
14618 void LoadHelpTextInfo(void)
14619 {
14620   char *filename = getHelpTextFilename();
14621   int i;
14622
14623   if (helptext_info != NULL)
14624   {
14625     freeSetupFileHash(helptext_info);
14626     helptext_info = NULL;
14627   }
14628
14629   if (fileExists(filename))
14630     helptext_info = loadSetupFileHash(filename);
14631
14632   if (helptext_info == NULL)
14633   {
14634     // use reliable default values from static configuration
14635     helptext_info = newSetupFileHash();
14636
14637     for (i = 0; helptext_config[i].token; i++)
14638       setHashEntry(helptext_info,
14639                    helptext_config[i].token,
14640                    helptext_config[i].value);
14641   }
14642
14643 #if 0
14644   BEGIN_HASH_ITERATION(helptext_info, itr)
14645   {
14646     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14647           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14648   }
14649   END_HASH_ITERATION(hash, itr)
14650 #endif
14651 }
14652
14653
14654 // ----------------------------------------------------------------------------
14655 // convert levels
14656 // ----------------------------------------------------------------------------
14657
14658 #define MAX_NUM_CONVERT_LEVELS          1000
14659
14660 void ConvertLevels(void)
14661 {
14662   static LevelDirTree *convert_leveldir = NULL;
14663   static int convert_level_nr = -1;
14664   static int num_levels_handled = 0;
14665   static int num_levels_converted = 0;
14666   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14667   int i;
14668
14669   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14670                                                global.convert_leveldir);
14671
14672   if (convert_leveldir == NULL)
14673     Fail("no such level identifier: '%s'", global.convert_leveldir);
14674
14675   leveldir_current = convert_leveldir;
14676
14677   if (global.convert_level_nr != -1)
14678   {
14679     convert_leveldir->first_level = global.convert_level_nr;
14680     convert_leveldir->last_level  = global.convert_level_nr;
14681   }
14682
14683   convert_level_nr = convert_leveldir->first_level;
14684
14685   PrintLine("=", 79);
14686   Print("Converting levels\n");
14687   PrintLine("-", 79);
14688   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14689   Print("Level series name:       '%s'\n", convert_leveldir->name);
14690   Print("Level series author:     '%s'\n", convert_leveldir->author);
14691   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14692   PrintLine("=", 79);
14693   Print("\n");
14694
14695   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14696     levels_failed[i] = FALSE;
14697
14698   while (convert_level_nr <= convert_leveldir->last_level)
14699   {
14700     char *level_filename;
14701     boolean new_level;
14702
14703     level_nr = convert_level_nr++;
14704
14705     Print("Level %03d: ", level_nr);
14706
14707     LoadLevel(level_nr);
14708     if (level.no_level_file || level.no_valid_file)
14709     {
14710       Print("(no level)\n");
14711       continue;
14712     }
14713
14714     Print("converting level ... ");
14715
14716 #if 0
14717     // special case: conversion of some EMC levels as requested by ACME
14718     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14719 #endif
14720
14721     level_filename = getDefaultLevelFilename(level_nr);
14722     new_level = !fileExists(level_filename);
14723
14724     if (new_level)
14725     {
14726       SaveLevel(level_nr);
14727
14728       num_levels_converted++;
14729
14730       Print("converted.\n");
14731     }
14732     else
14733     {
14734       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14735         levels_failed[level_nr] = TRUE;
14736
14737       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14738     }
14739
14740     num_levels_handled++;
14741   }
14742
14743   Print("\n");
14744   PrintLine("=", 79);
14745   Print("Number of levels handled: %d\n", num_levels_handled);
14746   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14747          (num_levels_handled ?
14748           num_levels_converted * 100 / num_levels_handled : 0));
14749   PrintLine("-", 79);
14750   Print("Summary (for automatic parsing by scripts):\n");
14751   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14752          convert_leveldir->identifier, num_levels_converted,
14753          num_levels_handled,
14754          (num_levels_handled ?
14755           num_levels_converted * 100 / num_levels_handled : 0));
14756
14757   if (num_levels_handled != num_levels_converted)
14758   {
14759     Print(", FAILED:");
14760     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14761       if (levels_failed[i])
14762         Print(" %03d", i);
14763   }
14764
14765   Print("\n");
14766   PrintLine("=", 79);
14767
14768   CloseAllAndExit(0);
14769 }
14770
14771
14772 // ----------------------------------------------------------------------------
14773 // create and save images for use in level sketches (raw BMP format)
14774 // ----------------------------------------------------------------------------
14775
14776 void CreateLevelSketchImages(void)
14777 {
14778   Bitmap *bitmap1;
14779   Bitmap *bitmap2;
14780   int i;
14781
14782   InitElementPropertiesGfxElement();
14783
14784   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14785   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14786
14787   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14788   {
14789     int element = getMappedElement(i);
14790     char basename1[16];
14791     char basename2[16];
14792     char *filename1;
14793     char *filename2;
14794
14795     sprintf(basename1, "%04d.bmp", i);
14796     sprintf(basename2, "%04ds.bmp", i);
14797
14798     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14799     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14800
14801     DrawSizedElement(0, 0, element, TILESIZE);
14802     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14803
14804     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14805       Fail("cannot save level sketch image file '%s'", filename1);
14806
14807     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14808     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14809
14810     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14811       Fail("cannot save level sketch image file '%s'", filename2);
14812
14813     free(filename1);
14814     free(filename2);
14815
14816     // create corresponding SQL statements (for normal and small images)
14817     if (i < 1000)
14818     {
14819       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14820       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14821     }
14822
14823     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14824     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14825
14826     // optional: create content for forum level sketch demonstration post
14827     if (options.debug)
14828       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14829   }
14830
14831   FreeBitmap(bitmap1);
14832   FreeBitmap(bitmap2);
14833
14834   if (options.debug)
14835     fprintf(stderr, "\n");
14836
14837   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14838
14839   CloseAllAndExit(0);
14840 }
14841
14842
14843 // ----------------------------------------------------------------------------
14844 // create and save images for element collecting animations (raw BMP format)
14845 // ----------------------------------------------------------------------------
14846
14847 static boolean createCollectImage(int element)
14848 {
14849   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14850 }
14851
14852 void CreateCollectElementImages(void)
14853 {
14854   int i, j;
14855   int num_steps = 8;
14856   int anim_frames = num_steps - 1;
14857   int tile_size = TILESIZE;
14858   int anim_width  = tile_size * anim_frames;
14859   int anim_height = tile_size;
14860   int num_collect_images = 0;
14861   int pos_collect_images = 0;
14862
14863   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14864     if (createCollectImage(i))
14865       num_collect_images++;
14866
14867   Info("Creating %d element collecting animation images ...",
14868        num_collect_images);
14869
14870   int dst_width  = anim_width * 2;
14871   int dst_height = anim_height * num_collect_images / 2;
14872   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14873   char *basename_bmp = "RocksCollect.bmp";
14874   char *basename_png = "RocksCollect.png";
14875   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14876   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14877   int len_filename_bmp = strlen(filename_bmp);
14878   int len_filename_png = strlen(filename_png);
14879   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14880   char cmd_convert[max_command_len];
14881
14882   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14883            filename_bmp,
14884            filename_png);
14885
14886   // force using RGBA surface for destination bitmap
14887   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14888                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14889
14890   dst_bitmap->surface =
14891     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14892
14893   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14894   {
14895     if (!createCollectImage(i))
14896       continue;
14897
14898     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14899     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14900     int graphic = el2img(i);
14901     char *token_name = element_info[i].token_name;
14902     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14903     Bitmap *src_bitmap;
14904     int src_x, src_y;
14905
14906     Info("- creating collecting image for '%s' ...", token_name);
14907
14908     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14909
14910     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14911                tile_size, tile_size, 0, 0);
14912
14913     // force using RGBA surface for temporary bitmap (using transparent black)
14914     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14915                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14916
14917     tmp_bitmap->surface =
14918       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14919
14920     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14921
14922     for (j = 0; j < anim_frames; j++)
14923     {
14924       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14925       int frame_size = frame_size_final * num_steps;
14926       int offset = (tile_size - frame_size_final) / 2;
14927       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14928
14929       while (frame_size > frame_size_final)
14930       {
14931         frame_size /= 2;
14932
14933         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14934
14935         FreeBitmap(frame_bitmap);
14936
14937         frame_bitmap = half_bitmap;
14938       }
14939
14940       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14941                        frame_size_final, frame_size_final,
14942                        dst_x + j * tile_size + offset, dst_y + offset);
14943
14944       FreeBitmap(frame_bitmap);
14945     }
14946
14947     tmp_bitmap->surface_masked = NULL;
14948
14949     FreeBitmap(tmp_bitmap);
14950
14951     pos_collect_images++;
14952   }
14953
14954   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14955     Fail("cannot save element collecting image file '%s'", filename_bmp);
14956
14957   FreeBitmap(dst_bitmap);
14958
14959   Info("Converting image file from BMP to PNG ...");
14960
14961   if (system(cmd_convert) != 0)
14962     Fail("converting image file failed");
14963
14964   unlink(filename_bmp);
14965
14966   Info("Done.");
14967
14968   CloseAllAndExit(0);
14969 }
14970
14971
14972 // ----------------------------------------------------------------------------
14973 // create and save images for custom and group elements (raw BMP format)
14974 // ----------------------------------------------------------------------------
14975
14976 void CreateCustomElementImages(char *directory)
14977 {
14978   char *src_basename = "RocksCE-template.ilbm";
14979   char *dst_basename = "RocksCE.bmp";
14980   char *src_filename = getPath2(directory, src_basename);
14981   char *dst_filename = getPath2(directory, dst_basename);
14982   Bitmap *src_bitmap;
14983   Bitmap *bitmap;
14984   int yoffset_ce = 0;
14985   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14986   int i;
14987
14988   InitVideoDefaults();
14989
14990   ReCreateBitmap(&backbuffer, video.width, video.height);
14991
14992   src_bitmap = LoadImage(src_filename);
14993
14994   bitmap = CreateBitmap(TILEX * 16 * 2,
14995                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14996                         DEFAULT_DEPTH);
14997
14998   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14999   {
15000     int x = i % 16;
15001     int y = i / 16;
15002     int ii = i + 1;
15003     int j;
15004
15005     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15006                TILEX * x, TILEY * y + yoffset_ce);
15007
15008     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15009                TILEX, TILEY,
15010                TILEX * x + TILEX * 16,
15011                TILEY * y + yoffset_ce);
15012
15013     for (j = 2; j >= 0; j--)
15014     {
15015       int c = ii % 10;
15016
15017       BlitBitmap(src_bitmap, bitmap,
15018                  TILEX + c * 7, 0, 6, 10,
15019                  TILEX * x + 6 + j * 7,
15020                  TILEY * y + 11 + yoffset_ce);
15021
15022       BlitBitmap(src_bitmap, bitmap,
15023                  TILEX + c * 8, TILEY, 6, 10,
15024                  TILEX * 16 + TILEX * x + 6 + j * 8,
15025                  TILEY * y + 10 + yoffset_ce);
15026
15027       ii /= 10;
15028     }
15029   }
15030
15031   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15032   {
15033     int x = i % 16;
15034     int y = i / 16;
15035     int ii = i + 1;
15036     int j;
15037
15038     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15039                TILEX * x, TILEY * y + yoffset_ge);
15040
15041     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15042                TILEX, TILEY,
15043                TILEX * x + TILEX * 16,
15044                TILEY * y + yoffset_ge);
15045
15046     for (j = 1; j >= 0; j--)
15047     {
15048       int c = ii % 10;
15049
15050       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15051                  TILEX * x + 6 + j * 10,
15052                  TILEY * y + 11 + yoffset_ge);
15053
15054       BlitBitmap(src_bitmap, bitmap,
15055                  TILEX + c * 8, TILEY + 12, 6, 10,
15056                  TILEX * 16 + TILEX * x + 10 + j * 8,
15057                  TILEY * y + 10 + yoffset_ge);
15058
15059       ii /= 10;
15060     }
15061   }
15062
15063   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15064     Fail("cannot save CE graphics file '%s'", dst_filename);
15065
15066   FreeBitmap(bitmap);
15067
15068   CloseAllAndExit(0);
15069 }