extended support for explosion 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 "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
311     &li.bd_cave_random_seed_c64,        0
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (some values for BD style amoeba following below)
606   {
607     EL_BD_PLAYER,                       -1,
608     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
609     &li.bd_diagonal_movements,          FALSE
610   },
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
614     &li.bd_topmost_player_active,       TRUE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
619     &li.bd_pushing_prob,                25
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
624     &li.bd_pushing_prob_with_sweet,     100
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
629     &li.bd_push_mega_rock_with_sweet,   FALSE
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
634     &li.bd_snap_element,                EL_EMPTY
635   },
636
637   {
638     EL_BD_SAND,                         -1,
639     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
640     &li.bd_sand_looks_like,             EL_BD_SAND
641   },
642
643   {
644     EL_BD_ROCK,                         -1,
645     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
646     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
647   },
648   {
649     EL_BD_ROCK,                         -1,
650     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
651     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
652   },
653
654   {
655     EL_BD_DIAMOND,                      -1,
656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
657     &li.score[SC_DIAMOND_EXTRA],        20
658   },
659   {
660     EL_BD_DIAMOND,                      -1,
661     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
662     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
663   },
664   {
665     EL_BD_DIAMOND,                      -1,
666     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
667     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
668   },
669
670   {
671     EL_BD_FIREFLY,                      -1,
672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
673     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
674   },
675
676   {
677     EL_BD_FIREFLY_2,                    -1,
678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
679     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
680   },
681
682   {
683     EL_BD_BUTTERFLY,                    -1,
684     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
685     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
686   },
687
688   {
689     EL_BD_BUTTERFLY_2,                  -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
691     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
692   },
693
694   {
695     EL_BD_STONEFLY,                     -1,
696     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
697     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
698   },
699
700   {
701     EL_BD_DRAGONFLY,                    -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
703     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
704   },
705
706   {
707     EL_BD_DIAMOND_GROWING_5,            -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
710   },
711
712   {
713     EL_BD_BOMB_EXPLODING_4,             -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
716   },
717
718   {
719     EL_BD_NITRO_PACK_EXPLODING_4,       -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
722   },
723
724   {
725     EL_BD_EXPLODING_5,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_explosion_turns_to,          EL_EMPTY
728   },
729
730   {
731     EL_BD_MAGIC_WALL,                   -1,
732     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
733     &li.bd_magic_wall_wait_hatching,    FALSE
734   },
735   {
736     EL_BD_MAGIC_WALL,                   -1,
737     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
738     &li.bd_magic_wall_stops_amoeba,     TRUE
739   },
740   {
741     EL_BD_MAGIC_WALL,                   -1,
742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
743     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
744   },
745   {
746     EL_BD_MAGIC_WALL,                   -1,
747     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
748     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
749   },
750   {
751     EL_BD_MAGIC_WALL,                   -1,
752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
753     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
754   },
755   {
756     EL_BD_MAGIC_WALL,                   -1,
757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
758     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
759   },
760   {
761     EL_BD_MAGIC_WALL,                   -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
763     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
764   },
765   {
766     EL_BD_MAGIC_WALL,                   -1,
767     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
768     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
769   },
770   {
771     EL_BD_MAGIC_WALL,                   -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
773     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
774   },
775
776   {
777     EL_BD_CLOCK,                        -1,
778     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
779     &li.bd_clock_extra_time,            30
780   },
781
782   {
783     EL_BD_VOODOO_DOLL,                  -1,
784     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
785     &li.bd_voodoo_collects_diamonds,    FALSE
786   },
787   {
788     EL_BD_VOODOO_DOLL,                  -1,
789     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
790     &li.bd_voodoo_hurt_kills_player,    FALSE
791   },
792   {
793     EL_BD_VOODOO_DOLL,                  -1,
794     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
795     &li.bd_voodoo_dies_by_rock,         FALSE
796   },
797   {
798     EL_BD_VOODOO_DOLL,                  -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
800     &li.bd_voodoo_vanish_by_explosion,  TRUE
801   },
802   {
803     EL_BD_VOODOO_DOLL,                  -1,
804     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
805     &li.bd_voodoo_penalty_time,         30
806   },
807
808   {
809     EL_BD_SLIME,                        -1,
810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
811     &li.bd_slime_is_predictable,        TRUE
812   },
813   {
814     EL_BD_SLIME,                        -1,
815     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
816     &li.bd_slime_permeability_rate,     100
817   },
818   {
819     EL_BD_SLIME,                        -1,
820     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
821     &li.bd_slime_permeability_bits_c64, 0
822   },
823   {
824     EL_BD_SLIME,                        -1,
825     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
826     &li.bd_slime_random_seed_c64,       -1
827   },
828   {
829     EL_BD_SLIME,                        -1,
830     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
831     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
832   },
833   {
834     EL_BD_SLIME,                        -1,
835     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
836     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
837   },
838   {
839     EL_BD_SLIME,                        -1,
840     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
841     &li.bd_slime_eats_element_2,        EL_BD_ROCK
842   },
843   {
844     EL_BD_SLIME,                        -1,
845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
846     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
847   },
848   {
849     EL_BD_SLIME,                        -1,
850     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
851     &li.bd_slime_eats_element_3,        EL_BD_NUT
852   },
853   {
854     EL_BD_SLIME,                        -1,
855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
856     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
857   },
858
859   {
860     EL_BD_ACID,                         -1,
861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
862     &li.bd_acid_eats_element,           EL_BD_SAND
863   },
864   {
865     EL_BD_ACID,                         -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
867     &li.bd_acid_spread_rate,            3
868   },
869   {
870     EL_BD_ACID,                         -1,
871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
872     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
873   },
874
875   {
876     EL_BD_BITER,                        -1,
877     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
878     &li.bd_biter_move_delay,            0
879   },
880   {
881     EL_BD_BITER,                        -1,
882     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
883     &li.bd_biter_eats_element,          EL_BD_DIAMOND
884   },
885
886   {
887     EL_BD_BLADDER,                      -1,
888     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
889     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
890   },
891
892   {
893     EL_BD_EXPANDABLE_WALL_ANY,          -1,
894     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
895     &li.bd_change_expanding_wall,       FALSE
896   },
897   {
898     EL_BD_EXPANDABLE_WALL_ANY,          -1,
899     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
900     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
901   },
902
903   {
904     EL_BD_REPLICATOR,                   -1,
905     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
906     &li.bd_replicators_active,          TRUE
907   },
908   {
909     EL_BD_REPLICATOR,                   -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
911     &li.bd_replicator_create_delay,     4
912   },
913
914   {
915     EL_BD_CONVEYOR_LEFT,                -1,
916     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
917     &li.bd_conveyor_belts_active,       TRUE
918   },
919   {
920     EL_BD_CONVEYOR_LEFT,                -1,
921     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
922     &li.bd_conveyor_belts_changed,      FALSE
923   },
924
925   {
926     EL_BD_WATER,                        -1,
927     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
928     &li.bd_water_cannot_flow_down,      FALSE
929   },
930
931   {
932     EL_BD_NUT,                          -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
935   },
936
937   {
938     EL_BD_PNEUMATIC_HAMMER,             -1,
939     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
940     &li.bd_hammer_walls_break_delay,    5
941   },
942   {
943     EL_BD_PNEUMATIC_HAMMER,             -1,
944     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
945     &li.bd_hammer_walls_reappear,       FALSE
946   },
947   {
948     EL_BD_PNEUMATIC_HAMMER,             -1,
949     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
950     &li.bd_hammer_walls_reappear_delay, 100
951   },
952
953   {
954     EL_BD_SKELETON,                     -1,
955     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
956     &li.bd_num_skeletons_needed_for_pot, 5
957   },
958   {
959     EL_BD_SKELETON,                     -1,
960     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
961     &li.bd_skeleton_worth_num_diamonds, 0
962   },
963
964   {
965     EL_BD_CREATURE_SWITCH,              -1,
966     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
967     &li.bd_creatures_start_backwards,   FALSE
968   },
969   {
970     EL_BD_CREATURE_SWITCH,              -1,
971     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
972     &li.bd_creatures_turn_on_hatching,  FALSE
973   },
974   {
975     EL_BD_CREATURE_SWITCH,              -1,
976     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
977     &li.bd_creatures_auto_turn_delay,   0
978   },
979
980   {
981     EL_BD_GRAVITY_SWITCH,               -1,
982     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
983     &li.bd_gravity_direction,           GD_MV_DOWN
984   },
985   {
986     EL_BD_GRAVITY_SWITCH,               -1,
987     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
988     &li.bd_gravity_switch_active,       FALSE
989   },
990   {
991     EL_BD_GRAVITY_SWITCH,               -1,
992     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
993     &li.bd_gravity_switch_delay,        10
994   },
995   {
996     EL_BD_GRAVITY_SWITCH,               -1,
997     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
998     &li.bd_gravity_affects_all,         TRUE
999   },
1000
1001   // (the following values are related to various game elements)
1002
1003   {
1004     EL_EMERALD,                         -1,
1005     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1006     &li.score[SC_EMERALD],              10
1007   },
1008
1009   {
1010     EL_DIAMOND,                         -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.score[SC_DIAMOND],              10
1013   },
1014
1015   {
1016     EL_BUG,                             -1,
1017     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1018     &li.score[SC_BUG],                  10
1019   },
1020
1021   {
1022     EL_SPACESHIP,                       -1,
1023     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1024     &li.score[SC_SPACESHIP],            10
1025   },
1026
1027   {
1028     EL_PACMAN,                          -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.score[SC_PACMAN],               10
1031   },
1032
1033   {
1034     EL_NUT,                             -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.score[SC_NUT],                  10
1037   },
1038
1039   {
1040     EL_DYNAMITE,                        -1,
1041     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1042     &li.score[SC_DYNAMITE],             10
1043   },
1044
1045   {
1046     EL_KEY_1,                           -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1048     &li.score[SC_KEY],                  10
1049   },
1050
1051   {
1052     EL_PEARL,                           -1,
1053     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1054     &li.score[SC_PEARL],                10
1055   },
1056
1057   {
1058     EL_CRYSTAL,                         -1,
1059     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1060     &li.score[SC_CRYSTAL],              10
1061   },
1062
1063   // (amoeba values used by R'n'D game engine only)
1064   {
1065     EL_BD_AMOEBA,                       -1,
1066     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1067     &li.amoeba_content,                 EL_DIAMOND
1068   },
1069   {
1070     EL_BD_AMOEBA,                       -1,
1071     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1072     &li.amoeba_speed,                   10
1073   },
1074   {
1075     EL_BD_AMOEBA,                       -1,
1076     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1077     &li.grow_into_diggable,             TRUE
1078   },
1079   // (amoeba values used by BD game engine only)
1080   {
1081     EL_BD_AMOEBA,                       -1,
1082     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1083     &li.bd_amoeba_wait_for_hatching,    FALSE
1084   },
1085   {
1086     EL_BD_AMOEBA,                       -1,
1087     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1088     &li.bd_amoeba_start_immediately,    TRUE
1089   },
1090   {
1091     EL_BD_AMOEBA,                       -1,
1092     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1093     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1094   },
1095   {
1096     EL_BD_AMOEBA,                       -1,
1097     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1098     &li.bd_amoeba_threshold_too_big,    200
1099   },
1100   {
1101     EL_BD_AMOEBA,                       -1,
1102     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1103     &li.bd_amoeba_slow_growth_time,     200
1104   },
1105   {
1106     EL_BD_AMOEBA,                       -1,
1107     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1108     &li.bd_amoeba_slow_growth_rate,     3
1109   },
1110   {
1111     EL_BD_AMOEBA,                       -1,
1112     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1113     &li.bd_amoeba_fast_growth_rate,     25
1114   },
1115   {
1116     EL_BD_AMOEBA,                       -1,
1117     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1118     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1119   },
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1123     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1124   },
1125
1126   {
1127     EL_BD_AMOEBA_2,                     -1,
1128     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1129     &li.bd_amoeba_2_threshold_too_big,  200
1130   },
1131   {
1132     EL_BD_AMOEBA_2,                     -1,
1133     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1134     &li.bd_amoeba_2_slow_growth_time,   200
1135   },
1136   {
1137     EL_BD_AMOEBA_2,                     -1,
1138     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1139     &li.bd_amoeba_2_slow_growth_rate,   3
1140   },
1141   {
1142     EL_BD_AMOEBA_2,                     -1,
1143     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1144     &li.bd_amoeba_2_fast_growth_rate,   25
1145   },
1146   {
1147     EL_BD_AMOEBA_2,                     -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1149     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1150   },
1151   {
1152     EL_BD_AMOEBA_2,                     -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1154     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1155   },
1156   {
1157     EL_BD_AMOEBA_2,                     -1,
1158     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1159     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1160   },
1161   {
1162     EL_BD_AMOEBA_2,                     -1,
1163     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1164     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1165   },
1166
1167   {
1168     EL_YAMYAM,                          -1,
1169     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1170     &li.yamyam_content,                 EL_ROCK, NULL,
1171     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1172   },
1173   {
1174     EL_YAMYAM,                          -1,
1175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1176     &li.score[SC_YAMYAM],               10
1177   },
1178
1179   {
1180     EL_ROBOT,                           -1,
1181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1182     &li.score[SC_ROBOT],                10
1183   },
1184   {
1185     EL_ROBOT,                           -1,
1186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1187     &li.slurp_score,                    10
1188   },
1189
1190   {
1191     EL_ROBOT_WHEEL,                     -1,
1192     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1193     &li.time_wheel,                     10
1194   },
1195
1196   {
1197     EL_MAGIC_WALL,                      -1,
1198     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1199     &li.time_magic_wall,                10
1200   },
1201
1202   {
1203     EL_GAME_OF_LIFE,                    -1,
1204     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1205     &li.game_of_life[0],                2
1206   },
1207   {
1208     EL_GAME_OF_LIFE,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1210     &li.game_of_life[1],                3
1211   },
1212   {
1213     EL_GAME_OF_LIFE,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1215     &li.game_of_life[2],                3
1216   },
1217   {
1218     EL_GAME_OF_LIFE,                    -1,
1219     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1220     &li.game_of_life[3],                3
1221   },
1222   {
1223     EL_GAME_OF_LIFE,                    -1,
1224     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1225     &li.use_life_bugs,                  FALSE
1226   },
1227
1228   {
1229     EL_BIOMAZE,                         -1,
1230     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1231     &li.biomaze[0],                     2
1232   },
1233   {
1234     EL_BIOMAZE,                         -1,
1235     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1236     &li.biomaze[1],                     3
1237   },
1238   {
1239     EL_BIOMAZE,                         -1,
1240     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1241     &li.biomaze[2],                     3
1242   },
1243   {
1244     EL_BIOMAZE,                         -1,
1245     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1246     &li.biomaze[3],                     3
1247   },
1248
1249   {
1250     EL_TIMEGATE_SWITCH,                 -1,
1251     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1252     &li.time_timegate,                  10
1253   },
1254
1255   {
1256     EL_LIGHT_SWITCH_ACTIVE,             -1,
1257     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1258     &li.time_light,                     10
1259   },
1260
1261   {
1262     EL_SHIELD_NORMAL,                   -1,
1263     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1264     &li.shield_normal_time,             10
1265   },
1266   {
1267     EL_SHIELD_NORMAL,                   -1,
1268     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1269     &li.score[SC_SHIELD],               10
1270   },
1271
1272   {
1273     EL_SHIELD_DEADLY,                   -1,
1274     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1275     &li.shield_deadly_time,             10
1276   },
1277   {
1278     EL_SHIELD_DEADLY,                   -1,
1279     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1280     &li.score[SC_SHIELD],               10
1281   },
1282
1283   {
1284     EL_EXTRA_TIME,                      -1,
1285     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1286     &li.extra_time,                     10
1287   },
1288   {
1289     EL_EXTRA_TIME,                      -1,
1290     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1291     &li.extra_time_score,               10
1292   },
1293
1294   {
1295     EL_TIME_ORB_FULL,                   -1,
1296     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1297     &li.time_orb_time,                  10
1298   },
1299   {
1300     EL_TIME_ORB_FULL,                   -1,
1301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1302     &li.use_time_orb_bug,               FALSE
1303   },
1304
1305   {
1306     EL_SPRING,                          -1,
1307     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1308     &li.use_spring_bug,                 FALSE
1309   },
1310
1311   {
1312     EL_EMC_ANDROID,                     -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.android_move_time,              10
1315   },
1316   {
1317     EL_EMC_ANDROID,                     -1,
1318     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1319     &li.android_clone_time,             10
1320   },
1321   {
1322     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1323     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1324     &li.android_clone_element[0],       EL_EMPTY, NULL,
1325     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1326   },
1327   {
1328     EL_EMC_ANDROID,                     -1,
1329     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1330     &li.android_clone_element[0],       EL_EMPTY, NULL,
1331     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1332   },
1333
1334   {
1335     EL_EMC_LENSES,                      -1,
1336     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1337     &li.lenses_score,                   10
1338   },
1339   {
1340     EL_EMC_LENSES,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1342     &li.lenses_time,                    10
1343   },
1344
1345   {
1346     EL_EMC_MAGNIFIER,                   -1,
1347     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1348     &li.magnify_score,                  10
1349   },
1350   {
1351     EL_EMC_MAGNIFIER,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1353     &li.magnify_time,                   10
1354   },
1355
1356   {
1357     EL_EMC_MAGIC_BALL,                  -1,
1358     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1359     &li.ball_time,                      10
1360   },
1361   {
1362     EL_EMC_MAGIC_BALL,                  -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.ball_random,                    FALSE
1365   },
1366   {
1367     EL_EMC_MAGIC_BALL,                  -1,
1368     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1369     &li.ball_active_initial,            FALSE
1370   },
1371   {
1372     EL_EMC_MAGIC_BALL,                  -1,
1373     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1374     &li.ball_content,                   EL_EMPTY, NULL,
1375     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1376   },
1377
1378   {
1379     EL_SOKOBAN_FIELD_EMPTY,             -1,
1380     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1381     &li.sb_fields_needed,               TRUE
1382   },
1383
1384   {
1385     EL_SOKOBAN_OBJECT,                  -1,
1386     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1387     &li.sb_objects_needed,              TRUE
1388   },
1389
1390   {
1391     EL_MM_MCDUFFIN,                     -1,
1392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1393     &li.mm_laser_red,                   FALSE
1394   },
1395   {
1396     EL_MM_MCDUFFIN,                     -1,
1397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1398     &li.mm_laser_green,                 FALSE
1399   },
1400   {
1401     EL_MM_MCDUFFIN,                     -1,
1402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1403     &li.mm_laser_blue,                  TRUE
1404   },
1405
1406   {
1407     EL_DF_LASER,                        -1,
1408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1409     &li.df_laser_red,                   TRUE
1410   },
1411   {
1412     EL_DF_LASER,                        -1,
1413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1414     &li.df_laser_green,                 TRUE
1415   },
1416   {
1417     EL_DF_LASER,                        -1,
1418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1419     &li.df_laser_blue,                  FALSE
1420   },
1421
1422   {
1423     EL_MM_FUSE_ACTIVE,                  -1,
1424     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1425     &li.mm_time_fuse,                   25
1426   },
1427   {
1428     EL_MM_BOMB,                         -1,
1429     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1430     &li.mm_time_bomb,                   75
1431   },
1432
1433   {
1434     EL_MM_GRAY_BALL,                    -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1436     &li.mm_time_ball,                   75
1437   },
1438   {
1439     EL_MM_GRAY_BALL,                    -1,
1440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1441     &li.mm_ball_choice_mode,            ANIM_RANDOM
1442   },
1443   {
1444     EL_MM_GRAY_BALL,                    -1,
1445     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1446     &li.mm_ball_content,                EL_EMPTY, NULL,
1447     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1448   },
1449   {
1450     EL_MM_GRAY_BALL,                    -1,
1451     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1452     &li.rotate_mm_ball_content,         TRUE
1453   },
1454   {
1455     EL_MM_GRAY_BALL,                    -1,
1456     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1457     &li.explode_mm_ball,                FALSE
1458   },
1459
1460   {
1461     EL_MM_STEEL_BLOCK,                  -1,
1462     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1463     &li.mm_time_block,                  75
1464   },
1465   {
1466     EL_MM_LIGHTBALL,                    -1,
1467     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1468     &li.score[SC_ELEM_BONUS],           10
1469   },
1470
1471   {
1472     -1,                                 -1,
1473     -1,                                 -1,
1474     NULL,                               -1
1475   }
1476 };
1477
1478 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1479 {
1480   {
1481     -1,                                 -1,
1482     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1483     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1484   },
1485   {
1486     -1,                                 -1,
1487     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1488     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1489   },
1490
1491   {
1492     -1,                                 -1,
1493     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1494     &xx_envelope.autowrap,              FALSE
1495   },
1496   {
1497     -1,                                 -1,
1498     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1499     &xx_envelope.centered,              FALSE
1500   },
1501
1502   {
1503     -1,                                 -1,
1504     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1505     &xx_envelope.text,                  -1, NULL,
1506     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1507     &xx_default_string_empty[0]
1508   },
1509
1510   {
1511     -1,                                 -1,
1512     -1,                                 -1,
1513     NULL,                               -1
1514   }
1515 };
1516
1517 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1518 {
1519   {
1520     -1,                                 -1,
1521     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1522     &xx_ei.description[0],              -1,
1523     &yy_ei.description[0],
1524     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1525     &xx_default_description[0]
1526   },
1527
1528   {
1529     -1,                                 -1,
1530     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1531     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1532     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1533   },
1534 #if ENABLE_RESERVED_CODE
1535   // (reserved for later use)
1536   {
1537     -1,                                 -1,
1538     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1539     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1540     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1541   },
1542 #endif
1543
1544   {
1545     -1,                                 -1,
1546     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1547     &xx_ei.use_gfx_element,             FALSE,
1548     &yy_ei.use_gfx_element
1549   },
1550   {
1551     -1,                                 -1,
1552     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1553     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1554     &yy_ei.gfx_element_initial
1555   },
1556
1557   {
1558     -1,                                 -1,
1559     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1560     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1561     &yy_ei.access_direction
1562   },
1563
1564   {
1565     -1,                                 -1,
1566     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1567     &xx_ei.collect_score_initial,       10,
1568     &yy_ei.collect_score_initial
1569   },
1570   {
1571     -1,                                 -1,
1572     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1573     &xx_ei.collect_count_initial,       1,
1574     &yy_ei.collect_count_initial
1575   },
1576
1577   {
1578     -1,                                 -1,
1579     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1580     &xx_ei.ce_value_fixed_initial,      0,
1581     &yy_ei.ce_value_fixed_initial
1582   },
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1586     &xx_ei.ce_value_random_initial,     0,
1587     &yy_ei.ce_value_random_initial
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1592     &xx_ei.use_last_ce_value,           FALSE,
1593     &yy_ei.use_last_ce_value
1594   },
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1599     &xx_ei.push_delay_fixed,            8,
1600     &yy_ei.push_delay_fixed
1601   },
1602   {
1603     -1,                                 -1,
1604     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1605     &xx_ei.push_delay_random,           8,
1606     &yy_ei.push_delay_random
1607   },
1608   {
1609     -1,                                 -1,
1610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1611     &xx_ei.drop_delay_fixed,            0,
1612     &yy_ei.drop_delay_fixed
1613   },
1614   {
1615     -1,                                 -1,
1616     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1617     &xx_ei.drop_delay_random,           0,
1618     &yy_ei.drop_delay_random
1619   },
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1623     &xx_ei.move_delay_fixed,            0,
1624     &yy_ei.move_delay_fixed
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1629     &xx_ei.move_delay_random,           0,
1630     &yy_ei.move_delay_random
1631   },
1632   {
1633     -1,                                 -1,
1634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1635     &xx_ei.step_delay_fixed,            0,
1636     &yy_ei.step_delay_fixed
1637   },
1638   {
1639     -1,                                 -1,
1640     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1641     &xx_ei.step_delay_random,           0,
1642     &yy_ei.step_delay_random
1643   },
1644
1645   {
1646     -1,                                 -1,
1647     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1648     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1649     &yy_ei.move_pattern
1650   },
1651   {
1652     -1,                                 -1,
1653     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1654     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1655     &yy_ei.move_direction_initial
1656   },
1657   {
1658     -1,                                 -1,
1659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1660     &xx_ei.move_stepsize,               TILEX / 8,
1661     &yy_ei.move_stepsize
1662   },
1663
1664   {
1665     -1,                                 -1,
1666     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1667     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1668     &yy_ei.move_enter_element
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1673     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1674     &yy_ei.move_leave_element
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1679     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1680     &yy_ei.move_leave_type
1681   },
1682
1683   {
1684     -1,                                 -1,
1685     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1686     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1687     &yy_ei.slippery_type
1688   },
1689
1690   {
1691     -1,                                 -1,
1692     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1693     &xx_ei.explosion_type,              EXPLODES_3X3,
1694     &yy_ei.explosion_type
1695   },
1696   {
1697     -1,                                 -1,
1698     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1699     &xx_ei.explosion_delay,             16,
1700     &yy_ei.explosion_delay
1701   },
1702   {
1703     -1,                                 -1,
1704     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1705     &xx_ei.ignition_delay,              8,
1706     &yy_ei.ignition_delay
1707   },
1708
1709   {
1710     -1,                                 -1,
1711     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1712     &xx_ei.content,                     EL_EMPTY_SPACE,
1713     &yy_ei.content,
1714     &xx_num_contents,                   1, 1
1715   },
1716
1717   // ---------- "num_change_pages" must be the last entry ---------------------
1718
1719   {
1720     -1,                                 SAVE_CONF_ALWAYS,
1721     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1722     &xx_ei.num_change_pages,            1,
1723     &yy_ei.num_change_pages
1724   },
1725
1726   {
1727     -1,                                 -1,
1728     -1,                                 -1,
1729     NULL,                               -1,
1730     NULL
1731   }
1732 };
1733
1734 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1735 {
1736   // ---------- "current_change_page" must be the first entry -----------------
1737
1738   {
1739     -1,                                 SAVE_CONF_ALWAYS,
1740     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1741     &xx_current_change_page,            -1
1742   },
1743
1744   // ---------- (the remaining entries can be in any order) -------------------
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1749     &xx_change.can_change,              FALSE
1750   },
1751
1752   {
1753     -1,                                 -1,
1754     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1755     &xx_event_bits[0],                  0
1756   },
1757   {
1758     -1,                                 -1,
1759     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1760     &xx_event_bits[1],                  0
1761   },
1762
1763   {
1764     -1,                                 -1,
1765     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1766     &xx_change.trigger_player,          CH_PLAYER_ANY
1767   },
1768   {
1769     -1,                                 -1,
1770     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1771     &xx_change.trigger_side,            CH_SIDE_ANY
1772   },
1773   {
1774     -1,                                 -1,
1775     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1776     &xx_change.trigger_page,            CH_PAGE_ANY
1777   },
1778
1779   {
1780     -1,                                 -1,
1781     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1782     &xx_change.target_element,          EL_EMPTY_SPACE
1783   },
1784
1785   {
1786     -1,                                 -1,
1787     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1788     &xx_change.delay_fixed,             0
1789   },
1790   {
1791     -1,                                 -1,
1792     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1793     &xx_change.delay_random,            0
1794   },
1795   {
1796     -1,                                 -1,
1797     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1798     &xx_change.delay_frames,            FRAMES_PER_SECOND
1799   },
1800
1801   {
1802     -1,                                 -1,
1803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1804     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1805   },
1806
1807   {
1808     -1,                                 -1,
1809     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1810     &xx_change.explode,                 FALSE
1811   },
1812   {
1813     -1,                                 -1,
1814     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1815     &xx_change.use_target_content,      FALSE
1816   },
1817   {
1818     -1,                                 -1,
1819     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1820     &xx_change.only_if_complete,        FALSE
1821   },
1822   {
1823     -1,                                 -1,
1824     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1825     &xx_change.use_random_replace,      FALSE
1826   },
1827   {
1828     -1,                                 -1,
1829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1830     &xx_change.random_percentage,       100
1831   },
1832   {
1833     -1,                                 -1,
1834     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1835     &xx_change.replace_when,            CP_WHEN_EMPTY
1836   },
1837
1838   {
1839     -1,                                 -1,
1840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1841     &xx_change.has_action,              FALSE
1842   },
1843   {
1844     -1,                                 -1,
1845     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1846     &xx_change.action_type,             CA_NO_ACTION
1847   },
1848   {
1849     -1,                                 -1,
1850     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1851     &xx_change.action_mode,             CA_MODE_UNDEFINED
1852   },
1853   {
1854     -1,                                 -1,
1855     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1856     &xx_change.action_arg,              CA_ARG_UNDEFINED
1857   },
1858
1859   {
1860     -1,                                 -1,
1861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1862     &xx_change.action_element,          EL_EMPTY_SPACE
1863   },
1864
1865   {
1866     -1,                                 -1,
1867     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1868     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1869     &xx_num_contents,                   1, 1
1870   },
1871
1872   {
1873     -1,                                 -1,
1874     -1,                                 -1,
1875     NULL,                               -1
1876   }
1877 };
1878
1879 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1880 {
1881   {
1882     -1,                                 -1,
1883     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1884     &xx_ei.description[0],              -1, NULL,
1885     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1886     &xx_default_description[0]
1887   },
1888
1889   {
1890     -1,                                 -1,
1891     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1892     &xx_ei.use_gfx_element,             FALSE
1893   },
1894   {
1895     -1,                                 -1,
1896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1897     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1898   },
1899
1900   {
1901     -1,                                 -1,
1902     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1903     &xx_group.choice_mode,              ANIM_RANDOM
1904   },
1905
1906   {
1907     -1,                                 -1,
1908     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1909     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1910     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1911   },
1912
1913   {
1914     -1,                                 -1,
1915     -1,                                 -1,
1916     NULL,                               -1
1917   }
1918 };
1919
1920 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1921 {
1922   {
1923     -1,                                 -1,
1924     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1925     &xx_ei.use_gfx_element,             FALSE
1926   },
1927   {
1928     -1,                                 -1,
1929     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1930     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1931   },
1932
1933   {
1934     -1,                                 -1,
1935     -1,                                 -1,
1936     NULL,                               -1
1937   }
1938 };
1939
1940 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1941 {
1942   {
1943     EL_PLAYER_1,                        -1,
1944     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1945     &li.block_snap_field,               TRUE
1946   },
1947   {
1948     EL_PLAYER_1,                        -1,
1949     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1950     &li.continuous_snapping,            TRUE
1951   },
1952   {
1953     EL_PLAYER_1,                        -1,
1954     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1955     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1956   },
1957   {
1958     EL_PLAYER_1,                        -1,
1959     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1960     &li.use_start_element[0],           FALSE
1961   },
1962   {
1963     EL_PLAYER_1,                        -1,
1964     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1965     &li.start_element[0],               EL_PLAYER_1
1966   },
1967   {
1968     EL_PLAYER_1,                        -1,
1969     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1970     &li.use_artwork_element[0],         FALSE
1971   },
1972   {
1973     EL_PLAYER_1,                        -1,
1974     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1975     &li.artwork_element[0],             EL_PLAYER_1
1976   },
1977   {
1978     EL_PLAYER_1,                        -1,
1979     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1980     &li.use_explosion_element[0],       FALSE
1981   },
1982   {
1983     EL_PLAYER_1,                        -1,
1984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1985     &li.explosion_element[0],           EL_PLAYER_1
1986   },
1987
1988   {
1989     -1,                                 -1,
1990     -1,                                 -1,
1991     NULL,                               -1
1992   }
1993 };
1994
1995 static struct
1996 {
1997   int filetype;
1998   char *id;
1999 }
2000 filetype_id_list[] =
2001 {
2002   { LEVEL_FILE_TYPE_RND,        "RND"   },
2003   { LEVEL_FILE_TYPE_BD,         "BD"    },
2004   { LEVEL_FILE_TYPE_EM,         "EM"    },
2005   { LEVEL_FILE_TYPE_SP,         "SP"    },
2006   { LEVEL_FILE_TYPE_DX,         "DX"    },
2007   { LEVEL_FILE_TYPE_SB,         "SB"    },
2008   { LEVEL_FILE_TYPE_DC,         "DC"    },
2009   { LEVEL_FILE_TYPE_MM,         "MM"    },
2010   { LEVEL_FILE_TYPE_MM,         "DF"    },
2011   { -1,                         NULL    },
2012 };
2013
2014
2015 // ============================================================================
2016 // level file functions
2017 // ============================================================================
2018
2019 static boolean check_special_flags(char *flag)
2020 {
2021   if (strEqual(options.special_flags, flag) ||
2022       strEqual(leveldir_current->special_flags, flag))
2023     return TRUE;
2024
2025   return FALSE;
2026 }
2027
2028 static struct DateInfo getCurrentDate(void)
2029 {
2030   time_t epoch_seconds = time(NULL);
2031   struct tm *now = localtime(&epoch_seconds);
2032   struct DateInfo date;
2033
2034   date.year  = now->tm_year + 1900;
2035   date.month = now->tm_mon  + 1;
2036   date.day   = now->tm_mday;
2037
2038   date.src   = DATE_SRC_CLOCK;
2039
2040   return date;
2041 }
2042
2043 static void resetEventFlags(struct ElementChangeInfo *change)
2044 {
2045   int i;
2046
2047   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2048     change->has_event[i] = FALSE;
2049 }
2050
2051 static void resetEventBits(void)
2052 {
2053   int i;
2054
2055   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2056     xx_event_bits[i] = 0;
2057 }
2058
2059 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2060 {
2061   int i;
2062
2063   /* important: only change event flag if corresponding event bit is set
2064      (this is because all xx_event_bits[] values are loaded separately,
2065      and all xx_event_bits[] values are set back to zero before loading
2066      another value xx_event_bits[x] (each value representing 32 flags)) */
2067
2068   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2069     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2070       change->has_event[i] = TRUE;
2071 }
2072
2073 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2074 {
2075   int i;
2076
2077   /* in contrast to the above function setEventFlagsFromEventBits(), it
2078      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2079      depending on the corresponding change->has_event[i] values here, as
2080      all xx_event_bits[] values are reset in resetEventBits() before */
2081
2082   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2083     if (change->has_event[i])
2084       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2085 }
2086
2087 static char *getDefaultElementDescription(struct ElementInfo *ei)
2088 {
2089   static char description[MAX_ELEMENT_NAME_LEN + 1];
2090   char *default_description = (ei->custom_description != NULL ?
2091                                ei->custom_description :
2092                                ei->editor_description);
2093   int i;
2094
2095   // always start with reliable default values
2096   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2097     description[i] = '\0';
2098
2099   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2100   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2101
2102   return &description[0];
2103 }
2104
2105 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2106 {
2107   char *default_description = getDefaultElementDescription(ei);
2108   int i;
2109
2110   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2111     ei->description[i] = default_description[i];
2112 }
2113
2114 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2115 {
2116   int i;
2117
2118   for (i = 0; conf[i].data_type != -1; i++)
2119   {
2120     int default_value = conf[i].default_value;
2121     int data_type = conf[i].data_type;
2122     int conf_type = conf[i].conf_type;
2123     int byte_mask = conf_type & CONF_MASK_BYTES;
2124
2125     if (byte_mask == CONF_MASK_MULTI_BYTES)
2126     {
2127       int default_num_entities = conf[i].default_num_entities;
2128       int max_num_entities = conf[i].max_num_entities;
2129
2130       *(int *)(conf[i].num_entities) = default_num_entities;
2131
2132       if (data_type == TYPE_STRING)
2133       {
2134         char *default_string = conf[i].default_string;
2135         char *string = (char *)(conf[i].value);
2136
2137         strncpy(string, default_string, max_num_entities);
2138       }
2139       else if (data_type == TYPE_ELEMENT_LIST)
2140       {
2141         int *element_array = (int *)(conf[i].value);
2142         int j;
2143
2144         for (j = 0; j < max_num_entities; j++)
2145           element_array[j] = default_value;
2146       }
2147       else if (data_type == TYPE_CONTENT_LIST)
2148       {
2149         struct Content *content = (struct Content *)(conf[i].value);
2150         int c, x, y;
2151
2152         for (c = 0; c < max_num_entities; c++)
2153           for (y = 0; y < 3; y++)
2154             for (x = 0; x < 3; x++)
2155               content[c].e[x][y] = default_value;
2156       }
2157     }
2158     else        // constant size configuration data (1, 2 or 4 bytes)
2159     {
2160       if (data_type == TYPE_BOOLEAN)
2161         *(boolean *)(conf[i].value) = default_value;
2162       else
2163         *(int *)    (conf[i].value) = default_value;
2164     }
2165   }
2166 }
2167
2168 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2169 {
2170   int i;
2171
2172   for (i = 0; conf[i].data_type != -1; i++)
2173   {
2174     int data_type = conf[i].data_type;
2175     int conf_type = conf[i].conf_type;
2176     int byte_mask = conf_type & CONF_MASK_BYTES;
2177
2178     if (byte_mask == CONF_MASK_MULTI_BYTES)
2179     {
2180       int max_num_entities = conf[i].max_num_entities;
2181
2182       if (data_type == TYPE_STRING)
2183       {
2184         char *string      = (char *)(conf[i].value);
2185         char *string_copy = (char *)(conf[i].value_copy);
2186
2187         strncpy(string_copy, string, max_num_entities);
2188       }
2189       else if (data_type == TYPE_ELEMENT_LIST)
2190       {
2191         int *element_array      = (int *)(conf[i].value);
2192         int *element_array_copy = (int *)(conf[i].value_copy);
2193         int j;
2194
2195         for (j = 0; j < max_num_entities; j++)
2196           element_array_copy[j] = element_array[j];
2197       }
2198       else if (data_type == TYPE_CONTENT_LIST)
2199       {
2200         struct Content *content      = (struct Content *)(conf[i].value);
2201         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
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_copy[c].e[x][y] = content[c].e[x][y];
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_copy) = *(boolean *)(conf[i].value);
2214       else
2215         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2216     }
2217   }
2218 }
2219
2220 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2221 {
2222   int i;
2223
2224   xx_ei = *ei_from;     // copy element data into temporary buffer
2225   yy_ei = *ei_to;       // copy element data into temporary buffer
2226
2227   copyConfigFromConfigList(chunk_config_CUSX_base);
2228
2229   *ei_from = xx_ei;
2230   *ei_to   = yy_ei;
2231
2232   // ---------- reinitialize and copy change pages ----------
2233
2234   ei_to->num_change_pages = ei_from->num_change_pages;
2235   ei_to->current_change_page = ei_from->current_change_page;
2236
2237   setElementChangePages(ei_to, ei_to->num_change_pages);
2238
2239   for (i = 0; i < ei_to->num_change_pages; i++)
2240     ei_to->change_page[i] = ei_from->change_page[i];
2241
2242   // ---------- copy group element info ----------
2243   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2244     *ei_to->group = *ei_from->group;
2245
2246   // mark this custom element as modified
2247   ei_to->modified_settings = TRUE;
2248 }
2249
2250 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2251 {
2252   int change_page_size = sizeof(struct ElementChangeInfo);
2253
2254   ei->num_change_pages = MAX(1, change_pages);
2255
2256   ei->change_page =
2257     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2258
2259   if (ei->current_change_page >= ei->num_change_pages)
2260     ei->current_change_page = ei->num_change_pages - 1;
2261
2262   ei->change = &ei->change_page[ei->current_change_page];
2263 }
2264
2265 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2266 {
2267   xx_change = *change;          // copy change data into temporary buffer
2268
2269   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2270
2271   *change = xx_change;
2272
2273   resetEventFlags(change);
2274
2275   change->direct_action = 0;
2276   change->other_action = 0;
2277
2278   change->pre_change_function = NULL;
2279   change->change_function = NULL;
2280   change->post_change_function = NULL;
2281 }
2282
2283 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2284 {
2285   int i, x, y;
2286
2287   li = *level;          // copy level data into temporary buffer
2288   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2289   *level = li;          // copy temporary buffer back to level data
2290
2291   setLevelInfoToDefaults_BD();
2292   setLevelInfoToDefaults_EM();
2293   setLevelInfoToDefaults_SP();
2294   setLevelInfoToDefaults_MM();
2295
2296   level->native_bd_level = &native_bd_level;
2297   level->native_em_level = &native_em_level;
2298   level->native_sp_level = &native_sp_level;
2299   level->native_mm_level = &native_mm_level;
2300
2301   level->file_version = FILE_VERSION_ACTUAL;
2302   level->game_version = GAME_VERSION_ACTUAL;
2303
2304   level->creation_date = getCurrentDate();
2305
2306   level->encoding_16bit_field  = TRUE;
2307   level->encoding_16bit_yamyam = TRUE;
2308   level->encoding_16bit_amoeba = TRUE;
2309
2310   // clear level name and level author string buffers
2311   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2312     level->name[i] = '\0';
2313   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2314     level->author[i] = '\0';
2315
2316   // set level name and level author to default values
2317   strcpy(level->name, NAMELESS_LEVEL_NAME);
2318   strcpy(level->author, ANONYMOUS_NAME);
2319
2320   // set level playfield to playable default level with player and exit
2321   for (x = 0; x < MAX_LEV_FIELDX; x++)
2322     for (y = 0; y < MAX_LEV_FIELDY; y++)
2323       level->field[x][y] = EL_SAND;
2324
2325   level->field[0][0] = EL_PLAYER_1;
2326   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2327
2328   BorderElement = EL_STEELWALL;
2329
2330   // detect custom elements when loading them
2331   level->file_has_custom_elements = FALSE;
2332
2333   // set all bug compatibility flags to "false" => do not emulate this bug
2334   level->use_action_after_change_bug = FALSE;
2335
2336   if (leveldir_current)
2337   {
2338     // try to determine better author name than 'anonymous'
2339     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2340     {
2341       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2342       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2343     }
2344     else
2345     {
2346       switch (LEVELCLASS(leveldir_current))
2347       {
2348         case LEVELCLASS_TUTORIAL:
2349           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2350           break;
2351
2352         case LEVELCLASS_CONTRIB:
2353           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2354           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2355           break;
2356
2357         case LEVELCLASS_PRIVATE:
2358           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2359           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2360           break;
2361
2362         default:
2363           // keep default value
2364           break;
2365       }
2366     }
2367   }
2368 }
2369
2370 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2371 {
2372   static boolean clipboard_elements_initialized = FALSE;
2373   int i;
2374
2375   InitElementPropertiesStatic();
2376
2377   li = *level;          // copy level data into temporary buffer
2378   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2379   *level = li;          // copy temporary buffer back to level data
2380
2381   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2382   {
2383     int element = i;
2384     struct ElementInfo *ei = &element_info[element];
2385
2386     if (element == EL_MM_GRAY_BALL)
2387     {
2388       struct LevelInfo_MM *level_mm = level->native_mm_level;
2389       int j;
2390
2391       for (j = 0; j < level->num_mm_ball_contents; j++)
2392         level->mm_ball_content[j] =
2393           map_element_MM_to_RND(level_mm->ball_content[j]);
2394     }
2395
2396     // never initialize clipboard elements after the very first time
2397     // (to be able to use clipboard elements between several levels)
2398     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2399       continue;
2400
2401     if (IS_ENVELOPE(element))
2402     {
2403       int envelope_nr = element - EL_ENVELOPE_1;
2404
2405       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2406
2407       level->envelope[envelope_nr] = xx_envelope;
2408     }
2409
2410     if (IS_CUSTOM_ELEMENT(element) ||
2411         IS_GROUP_ELEMENT(element) ||
2412         IS_INTERNAL_ELEMENT(element))
2413     {
2414       xx_ei = *ei;      // copy element data into temporary buffer
2415
2416       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2417
2418       *ei = xx_ei;
2419     }
2420
2421     setElementChangePages(ei, 1);
2422     setElementChangeInfoToDefaults(ei->change);
2423
2424     if (IS_CUSTOM_ELEMENT(element) ||
2425         IS_GROUP_ELEMENT(element))
2426     {
2427       setElementDescriptionToDefault(ei);
2428
2429       ei->modified_settings = FALSE;
2430     }
2431
2432     if (IS_CUSTOM_ELEMENT(element) ||
2433         IS_INTERNAL_ELEMENT(element))
2434     {
2435       // internal values used in level editor
2436
2437       ei->access_type = 0;
2438       ei->access_layer = 0;
2439       ei->access_protected = 0;
2440       ei->walk_to_action = 0;
2441       ei->smash_targets = 0;
2442       ei->deadliness = 0;
2443
2444       ei->can_explode_by_fire = FALSE;
2445       ei->can_explode_smashed = FALSE;
2446       ei->can_explode_impact = FALSE;
2447
2448       ei->current_change_page = 0;
2449     }
2450
2451     if (IS_GROUP_ELEMENT(element) ||
2452         IS_INTERNAL_ELEMENT(element))
2453     {
2454       struct ElementGroupInfo *group;
2455
2456       // initialize memory for list of elements in group
2457       if (ei->group == NULL)
2458         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2459
2460       group = ei->group;
2461
2462       xx_group = *group;        // copy group data into temporary buffer
2463
2464       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2465
2466       *group = xx_group;
2467     }
2468
2469     if (IS_EMPTY_ELEMENT(element) ||
2470         IS_INTERNAL_ELEMENT(element))
2471     {
2472       xx_ei = *ei;              // copy element data into temporary buffer
2473
2474       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2475
2476       *ei = xx_ei;
2477     }
2478   }
2479
2480   clipboard_elements_initialized = TRUE;
2481 }
2482
2483 static void setLevelInfoToDefaults(struct LevelInfo *level,
2484                                    boolean level_info_only,
2485                                    boolean reset_file_status)
2486 {
2487   setLevelInfoToDefaults_Level(level);
2488
2489   if (!level_info_only)
2490     setLevelInfoToDefaults_Elements(level);
2491
2492   if (reset_file_status)
2493   {
2494     level->no_valid_file = FALSE;
2495     level->no_level_file = FALSE;
2496   }
2497
2498   level->changed = FALSE;
2499 }
2500
2501 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2502 {
2503   level_file_info->nr = 0;
2504   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2505   level_file_info->packed = FALSE;
2506
2507   setString(&level_file_info->basename, NULL);
2508   setString(&level_file_info->filename, NULL);
2509 }
2510
2511 int getMappedElement_SB(int, boolean);
2512
2513 static void ActivateLevelTemplate(void)
2514 {
2515   int x, y;
2516
2517   if (check_special_flags("load_xsb_to_ces"))
2518   {
2519     // fill smaller playfields with padding "beyond border wall" elements
2520     if (level.fieldx < level_template.fieldx ||
2521         level.fieldy < level_template.fieldy)
2522     {
2523       short field[level.fieldx][level.fieldy];
2524       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2525       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2526       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2527       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2528
2529       // copy old playfield (which is smaller than the visible area)
2530       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2531         field[x][y] = level.field[x][y];
2532
2533       // fill new, larger playfield with "beyond border wall" elements
2534       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2535         level.field[x][y] = getMappedElement_SB('_', TRUE);
2536
2537       // copy the old playfield to the middle of the new playfield
2538       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2539         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2540
2541       level.fieldx = new_fieldx;
2542       level.fieldy = new_fieldy;
2543     }
2544   }
2545
2546   // Currently there is no special action needed to activate the template
2547   // data, because 'element_info' property settings overwrite the original
2548   // level data, while all other variables do not change.
2549
2550   // Exception: 'from_level_template' elements in the original level playfield
2551   // are overwritten with the corresponding elements at the same position in
2552   // playfield from the level template.
2553
2554   for (x = 0; x < level.fieldx; x++)
2555     for (y = 0; y < level.fieldy; y++)
2556       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2557         level.field[x][y] = level_template.field[x][y];
2558
2559   if (check_special_flags("load_xsb_to_ces"))
2560   {
2561     struct LevelInfo level_backup = level;
2562
2563     // overwrite all individual level settings from template level settings
2564     level = level_template;
2565
2566     // restore level file info
2567     level.file_info = level_backup.file_info;
2568
2569     // restore playfield size
2570     level.fieldx = level_backup.fieldx;
2571     level.fieldy = level_backup.fieldy;
2572
2573     // restore playfield content
2574     for (x = 0; x < level.fieldx; x++)
2575       for (y = 0; y < level.fieldy; y++)
2576         level.field[x][y] = level_backup.field[x][y];
2577
2578     // restore name and author from individual level
2579     strcpy(level.name,   level_backup.name);
2580     strcpy(level.author, level_backup.author);
2581
2582     // restore flag "use_custom_template"
2583     level.use_custom_template = level_backup.use_custom_template;
2584   }
2585 }
2586
2587 static boolean checkForPackageFromBasename_BD(char *basename)
2588 {
2589   // check for native BD level file extensions
2590   if (!strSuffixLower(basename, ".bd") &&
2591       !strSuffixLower(basename, ".bdr") &&
2592       !strSuffixLower(basename, ".brc") &&
2593       !strSuffixLower(basename, ".gds"))
2594     return FALSE;
2595
2596   // check for standard single-level BD files (like "001.bd")
2597   if (strSuffixLower(basename, ".bd") &&
2598       strlen(basename) == 6 &&
2599       basename[0] >= '0' && basename[0] <= '9' &&
2600       basename[1] >= '0' && basename[1] <= '9' &&
2601       basename[2] >= '0' && basename[2] <= '9')
2602     return FALSE;
2603
2604   // this is a level package in native BD file format
2605   return TRUE;
2606 }
2607
2608 static char *getLevelFilenameFromBasename(char *basename)
2609 {
2610   static char *filename = NULL;
2611
2612   checked_free(filename);
2613
2614   filename = getPath2(getCurrentLevelDir(), basename);
2615
2616   return filename;
2617 }
2618
2619 static int getFileTypeFromBasename(char *basename)
2620 {
2621   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2622
2623   static char *filename = NULL;
2624   struct stat file_status;
2625
2626   // ---------- try to determine file type from filename ----------
2627
2628   // check for typical filename of a Supaplex level package file
2629   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2630     return LEVEL_FILE_TYPE_SP;
2631
2632   // check for typical filename of a Diamond Caves II level package file
2633   if (strSuffixLower(basename, ".dc") ||
2634       strSuffixLower(basename, ".dc2"))
2635     return LEVEL_FILE_TYPE_DC;
2636
2637   // check for typical filename of a Sokoban level package file
2638   if (strSuffixLower(basename, ".xsb") &&
2639       strchr(basename, '%') == NULL)
2640     return LEVEL_FILE_TYPE_SB;
2641
2642   // check for typical filename of a Boulder Dash (GDash) level package file
2643   if (checkForPackageFromBasename_BD(basename))
2644     return LEVEL_FILE_TYPE_BD;
2645
2646   // ---------- try to determine file type from filesize ----------
2647
2648   checked_free(filename);
2649   filename = getPath2(getCurrentLevelDir(), basename);
2650
2651   if (stat(filename, &file_status) == 0)
2652   {
2653     // check for typical filesize of a Supaplex level package file
2654     if (file_status.st_size == 170496)
2655       return LEVEL_FILE_TYPE_SP;
2656   }
2657
2658   return LEVEL_FILE_TYPE_UNKNOWN;
2659 }
2660
2661 static int getFileTypeFromMagicBytes(char *filename, int type)
2662 {
2663   File *file;
2664
2665   if ((file = openFile(filename, MODE_READ)))
2666   {
2667     char chunk_name[CHUNK_ID_LEN + 1];
2668
2669     getFileChunkBE(file, chunk_name, NULL);
2670
2671     if (strEqual(chunk_name, "MMII") ||
2672         strEqual(chunk_name, "MIRR"))
2673       type = LEVEL_FILE_TYPE_MM;
2674
2675     closeFile(file);
2676   }
2677
2678   return type;
2679 }
2680
2681 static boolean checkForPackageFromBasename(char *basename)
2682 {
2683   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2684   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2685
2686   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2687 }
2688
2689 static char *getSingleLevelBasenameExt(int nr, char *extension)
2690 {
2691   static char basename[MAX_FILENAME_LEN];
2692
2693   if (nr < 0)
2694     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2695   else
2696     sprintf(basename, "%03d.%s", nr, extension);
2697
2698   return basename;
2699 }
2700
2701 static char *getSingleLevelBasename(int nr)
2702 {
2703   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2704 }
2705
2706 static char *getPackedLevelBasename(int type)
2707 {
2708   static char basename[MAX_FILENAME_LEN];
2709   char *directory = getCurrentLevelDir();
2710   Directory *dir;
2711   DirectoryEntry *dir_entry;
2712
2713   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2714
2715   if ((dir = openDirectory(directory)) == NULL)
2716   {
2717     Warn("cannot read current level directory '%s'", directory);
2718
2719     return basename;
2720   }
2721
2722   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2723   {
2724     char *entry_basename = dir_entry->basename;
2725     int entry_type = getFileTypeFromBasename(entry_basename);
2726
2727     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2728     {
2729       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2730           type == entry_type)
2731       {
2732         strcpy(basename, entry_basename);
2733
2734         break;
2735       }
2736     }
2737   }
2738
2739   closeDirectory(dir);
2740
2741   return basename;
2742 }
2743
2744 static char *getSingleLevelFilename(int nr)
2745 {
2746   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2747 }
2748
2749 #if ENABLE_UNUSED_CODE
2750 static char *getPackedLevelFilename(int type)
2751 {
2752   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2753 }
2754 #endif
2755
2756 char *getDefaultLevelFilename(int nr)
2757 {
2758   return getSingleLevelFilename(nr);
2759 }
2760
2761 #if ENABLE_UNUSED_CODE
2762 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2763                                                  int type)
2764 {
2765   lfi->type = type;
2766   lfi->packed = FALSE;
2767
2768   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2769   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2770 }
2771 #endif
2772
2773 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2774                                                  int type, char *format, ...)
2775 {
2776   static char basename[MAX_FILENAME_LEN];
2777   va_list ap;
2778
2779   va_start(ap, format);
2780   vsprintf(basename, format, ap);
2781   va_end(ap);
2782
2783   lfi->type = type;
2784   lfi->packed = FALSE;
2785
2786   setString(&lfi->basename, basename);
2787   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2788 }
2789
2790 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2791                                                  int type)
2792 {
2793   lfi->type = type;
2794   lfi->packed = TRUE;
2795
2796   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2797   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2798 }
2799
2800 static int getFiletypeFromID(char *filetype_id)
2801 {
2802   char *filetype_id_lower;
2803   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2804   int i;
2805
2806   if (filetype_id == NULL)
2807     return LEVEL_FILE_TYPE_UNKNOWN;
2808
2809   filetype_id_lower = getStringToLower(filetype_id);
2810
2811   for (i = 0; filetype_id_list[i].id != NULL; i++)
2812   {
2813     char *id_lower = getStringToLower(filetype_id_list[i].id);
2814     
2815     if (strEqual(filetype_id_lower, id_lower))
2816       filetype = filetype_id_list[i].filetype;
2817
2818     free(id_lower);
2819
2820     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2821       break;
2822   }
2823
2824   free(filetype_id_lower);
2825
2826   return filetype;
2827 }
2828
2829 char *getLocalLevelTemplateFilename(void)
2830 {
2831   return getDefaultLevelFilename(-1);
2832 }
2833
2834 char *getGlobalLevelTemplateFilename(void)
2835 {
2836   // global variable "leveldir_current" must be modified in the loop below
2837   LevelDirTree *leveldir_current_last = leveldir_current;
2838   char *filename = NULL;
2839
2840   // check for template level in path from current to topmost tree node
2841
2842   while (leveldir_current != NULL)
2843   {
2844     filename = getDefaultLevelFilename(-1);
2845
2846     if (fileExists(filename))
2847       break;
2848
2849     leveldir_current = leveldir_current->node_parent;
2850   }
2851
2852   // restore global variable "leveldir_current" modified in above loop
2853   leveldir_current = leveldir_current_last;
2854
2855   return filename;
2856 }
2857
2858 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2859 {
2860   int nr = lfi->nr;
2861
2862   // special case: level number is negative => check for level template file
2863   if (nr < 0)
2864   {
2865     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2866                                          getSingleLevelBasename(-1));
2867
2868     // replace local level template filename with global template filename
2869     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2870
2871     // no fallback if template file not existing
2872     return;
2873   }
2874
2875   // special case: check for file name/pattern specified in "levelinfo.conf"
2876   if (leveldir_current->level_filename != NULL)
2877   {
2878     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2879
2880     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2881                                          leveldir_current->level_filename, nr);
2882
2883     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2884
2885     if (fileExists(lfi->filename))
2886       return;
2887   }
2888   else if (leveldir_current->level_filetype != NULL)
2889   {
2890     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2891
2892     // check for specified native level file with standard file name
2893     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2894                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2895     if (fileExists(lfi->filename))
2896       return;
2897   }
2898
2899   // check for native Rocks'n'Diamonds level file
2900   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2901                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2902   if (fileExists(lfi->filename))
2903     return;
2904
2905   // check for native Boulder Dash level file
2906   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2907   if (fileExists(lfi->filename))
2908     return;
2909
2910   // check for Emerald Mine level file (V1)
2911   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2912                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2913   if (fileExists(lfi->filename))
2914     return;
2915   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2916                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2917   if (fileExists(lfi->filename))
2918     return;
2919
2920   // check for Emerald Mine level file (V2 to V5)
2921   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2922   if (fileExists(lfi->filename))
2923     return;
2924
2925   // check for Emerald Mine level file (V6 / single mode)
2926   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2927   if (fileExists(lfi->filename))
2928     return;
2929   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2930   if (fileExists(lfi->filename))
2931     return;
2932
2933   // check for Emerald Mine level file (V6 / teamwork mode)
2934   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2935   if (fileExists(lfi->filename))
2936     return;
2937   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2938   if (fileExists(lfi->filename))
2939     return;
2940
2941   // check for various packed level file formats
2942   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2943   if (fileExists(lfi->filename))
2944     return;
2945
2946   // no known level file found -- use default values (and fail later)
2947   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2948                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2949 }
2950
2951 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2952 {
2953   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2954     lfi->type = getFileTypeFromBasename(lfi->basename);
2955
2956   if (lfi->type == LEVEL_FILE_TYPE_RND)
2957     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2958 }
2959
2960 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2961 {
2962   // always start with reliable default values
2963   setFileInfoToDefaults(level_file_info);
2964
2965   level_file_info->nr = nr;     // set requested level number
2966
2967   determineLevelFileInfo_Filename(level_file_info);
2968   determineLevelFileInfo_Filetype(level_file_info);
2969 }
2970
2971 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2972                               struct LevelFileInfo *lfi_to)
2973 {
2974   lfi_to->nr     = lfi_from->nr;
2975   lfi_to->type   = lfi_from->type;
2976   lfi_to->packed = lfi_from->packed;
2977
2978   setString(&lfi_to->basename, lfi_from->basename);
2979   setString(&lfi_to->filename, lfi_from->filename);
2980 }
2981
2982 // ----------------------------------------------------------------------------
2983 // functions for loading R'n'D level
2984 // ----------------------------------------------------------------------------
2985
2986 int getMappedElement(int element)
2987 {
2988   // remap some (historic, now obsolete) elements
2989
2990   switch (element)
2991   {
2992     case EL_PLAYER_OBSOLETE:
2993       element = EL_PLAYER_1;
2994       break;
2995
2996     case EL_KEY_OBSOLETE:
2997       element = EL_KEY_1;
2998       break;
2999
3000     case EL_EM_KEY_1_FILE_OBSOLETE:
3001       element = EL_EM_KEY_1;
3002       break;
3003
3004     case EL_EM_KEY_2_FILE_OBSOLETE:
3005       element = EL_EM_KEY_2;
3006       break;
3007
3008     case EL_EM_KEY_3_FILE_OBSOLETE:
3009       element = EL_EM_KEY_3;
3010       break;
3011
3012     case EL_EM_KEY_4_FILE_OBSOLETE:
3013       element = EL_EM_KEY_4;
3014       break;
3015
3016     case EL_ENVELOPE_OBSOLETE:
3017       element = EL_ENVELOPE_1;
3018       break;
3019
3020     case EL_SP_EMPTY:
3021       element = EL_EMPTY;
3022       break;
3023
3024     default:
3025       if (element >= NUM_FILE_ELEMENTS)
3026       {
3027         Warn("invalid level element %d", element);
3028
3029         element = EL_UNKNOWN;
3030       }
3031       break;
3032   }
3033
3034   return element;
3035 }
3036
3037 static int getMappedElementByVersion(int element, int game_version)
3038 {
3039   // remap some elements due to certain game version
3040
3041   if (game_version <= VERSION_IDENT(2,2,0,0))
3042   {
3043     // map game font elements
3044     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3045                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3046                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3047                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3048   }
3049
3050   if (game_version < VERSION_IDENT(3,0,0,0))
3051   {
3052     // map Supaplex gravity tube elements
3053     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3054                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3055                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3056                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3057                element);
3058   }
3059
3060   return element;
3061 }
3062
3063 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3064 {
3065   level->file_version = getFileVersion(file);
3066   level->game_version = getFileVersion(file);
3067
3068   return chunk_size;
3069 }
3070
3071 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3072 {
3073   level->creation_date.year  = getFile16BitBE(file);
3074   level->creation_date.month = getFile8Bit(file);
3075   level->creation_date.day   = getFile8Bit(file);
3076
3077   level->creation_date.src   = DATE_SRC_LEVELFILE;
3078
3079   return chunk_size;
3080 }
3081
3082 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3083 {
3084   int initial_player_stepsize;
3085   int initial_player_gravity;
3086   int i, x, y;
3087
3088   level->fieldx = getFile8Bit(file);
3089   level->fieldy = getFile8Bit(file);
3090
3091   level->time           = getFile16BitBE(file);
3092   level->gems_needed    = getFile16BitBE(file);
3093
3094   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3095     level->name[i] = getFile8Bit(file);
3096   level->name[MAX_LEVEL_NAME_LEN] = 0;
3097
3098   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3099     level->score[i] = getFile8Bit(file);
3100
3101   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3102   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3103     for (y = 0; y < 3; y++)
3104       for (x = 0; x < 3; x++)
3105         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3106
3107   level->amoeba_speed           = getFile8Bit(file);
3108   level->time_magic_wall        = getFile8Bit(file);
3109   level->time_wheel             = getFile8Bit(file);
3110   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3111
3112   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3113                                    STEPSIZE_NORMAL);
3114
3115   for (i = 0; i < MAX_PLAYERS; i++)
3116     level->initial_player_stepsize[i] = initial_player_stepsize;
3117
3118   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3119
3120   for (i = 0; i < MAX_PLAYERS; i++)
3121     level->initial_player_gravity[i] = initial_player_gravity;
3122
3123   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3124   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3125
3126   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3127
3128   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3129   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130   level->can_move_into_acid_bits = getFile32BitBE(file);
3131   level->dont_collide_with_bits = getFile8Bit(file);
3132
3133   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3134   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135
3136   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3137   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3138   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3139
3140   level->game_engine_type       = getFile8Bit(file);
3141
3142   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3143
3144   return chunk_size;
3145 }
3146
3147 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3148 {
3149   int i;
3150
3151   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3152     level->name[i] = getFile8Bit(file);
3153   level->name[MAX_LEVEL_NAME_LEN] = 0;
3154
3155   return chunk_size;
3156 }
3157
3158 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3159 {
3160   int i;
3161
3162   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3163     level->author[i] = getFile8Bit(file);
3164   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3165
3166   return chunk_size;
3167 }
3168
3169 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3170 {
3171   int x, y;
3172   int chunk_size_expected = level->fieldx * level->fieldy;
3173
3174   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3175      stored with 16-bit encoding (and should be twice as big then).
3176      Even worse, playfield data was stored 16-bit when only yamyam content
3177      contained 16-bit elements and vice versa. */
3178
3179   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3180     chunk_size_expected *= 2;
3181
3182   if (chunk_size_expected != chunk_size)
3183   {
3184     ReadUnusedBytesFromFile(file, chunk_size);
3185     return chunk_size_expected;
3186   }
3187
3188   for (y = 0; y < level->fieldy; y++)
3189     for (x = 0; x < level->fieldx; x++)
3190       level->field[x][y] =
3191         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3192                          getFile8Bit(file));
3193   return chunk_size;
3194 }
3195
3196 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3197 {
3198   int i, x, y;
3199   int header_size = 4;
3200   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3201   int chunk_size_expected = header_size + content_size;
3202
3203   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3204      stored with 16-bit encoding (and should be twice as big then).
3205      Even worse, playfield data was stored 16-bit when only yamyam content
3206      contained 16-bit elements and vice versa. */
3207
3208   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3209     chunk_size_expected += content_size;
3210
3211   if (chunk_size_expected != chunk_size)
3212   {
3213     ReadUnusedBytesFromFile(file, chunk_size);
3214     return chunk_size_expected;
3215   }
3216
3217   getFile8Bit(file);
3218   level->num_yamyam_contents = getFile8Bit(file);
3219   getFile8Bit(file);
3220   getFile8Bit(file);
3221
3222   // correct invalid number of content fields -- should never happen
3223   if (level->num_yamyam_contents < 1 ||
3224       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3225     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3226
3227   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3228     for (y = 0; y < 3; y++)
3229       for (x = 0; x < 3; x++)
3230         level->yamyam_content[i].e[x][y] =
3231           getMappedElement(level->encoding_16bit_field ?
3232                            getFile16BitBE(file) : getFile8Bit(file));
3233   return chunk_size;
3234 }
3235
3236 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3237 {
3238   int i, x, y;
3239   int element;
3240   int num_contents;
3241   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3242
3243   element = getMappedElement(getFile16BitBE(file));
3244   num_contents = getFile8Bit(file);
3245
3246   getFile8Bit(file);    // content x size (unused)
3247   getFile8Bit(file);    // content y size (unused)
3248
3249   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3250
3251   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3252     for (y = 0; y < 3; y++)
3253       for (x = 0; x < 3; x++)
3254         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3255
3256   // correct invalid number of content fields -- should never happen
3257   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3258     num_contents = STD_ELEMENT_CONTENTS;
3259
3260   if (element == EL_YAMYAM)
3261   {
3262     level->num_yamyam_contents = num_contents;
3263
3264     for (i = 0; i < num_contents; i++)
3265       for (y = 0; y < 3; y++)
3266         for (x = 0; x < 3; x++)
3267           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3268   }
3269   else if (element == EL_BD_AMOEBA)
3270   {
3271     level->amoeba_content = content_array[0][0][0];
3272   }
3273   else
3274   {
3275     Warn("cannot load content for element '%d'", element);
3276   }
3277
3278   return chunk_size;
3279 }
3280
3281 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3282 {
3283   int i;
3284   int element;
3285   int envelope_nr;
3286   int envelope_len;
3287   int chunk_size_expected;
3288
3289   element = getMappedElement(getFile16BitBE(file));
3290   if (!IS_ENVELOPE(element))
3291     element = EL_ENVELOPE_1;
3292
3293   envelope_nr = element - EL_ENVELOPE_1;
3294
3295   envelope_len = getFile16BitBE(file);
3296
3297   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3298   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3299
3300   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3301
3302   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3303   if (chunk_size_expected != chunk_size)
3304   {
3305     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3306     return chunk_size_expected;
3307   }
3308
3309   for (i = 0; i < envelope_len; i++)
3310     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3311
3312   return chunk_size;
3313 }
3314
3315 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3316 {
3317   int num_changed_custom_elements = getFile16BitBE(file);
3318   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3319   int i;
3320
3321   if (chunk_size_expected != chunk_size)
3322   {
3323     ReadUnusedBytesFromFile(file, chunk_size - 2);
3324     return chunk_size_expected;
3325   }
3326
3327   for (i = 0; i < num_changed_custom_elements; i++)
3328   {
3329     int element = getMappedElement(getFile16BitBE(file));
3330     int properties = getFile32BitBE(file);
3331
3332     if (IS_CUSTOM_ELEMENT(element))
3333       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3334     else
3335       Warn("invalid custom element number %d", element);
3336
3337     // older game versions that wrote level files with CUS1 chunks used
3338     // different default push delay values (not yet stored in level file)
3339     element_info[element].push_delay_fixed = 2;
3340     element_info[element].push_delay_random = 8;
3341   }
3342
3343   level->file_has_custom_elements = TRUE;
3344
3345   return chunk_size;
3346 }
3347
3348 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3349 {
3350   int num_changed_custom_elements = getFile16BitBE(file);
3351   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3352   int i;
3353
3354   if (chunk_size_expected != chunk_size)
3355   {
3356     ReadUnusedBytesFromFile(file, chunk_size - 2);
3357     return chunk_size_expected;
3358   }
3359
3360   for (i = 0; i < num_changed_custom_elements; i++)
3361   {
3362     int element = getMappedElement(getFile16BitBE(file));
3363     int custom_target_element = getMappedElement(getFile16BitBE(file));
3364
3365     if (IS_CUSTOM_ELEMENT(element))
3366       element_info[element].change->target_element = custom_target_element;
3367     else
3368       Warn("invalid custom element number %d", element);
3369   }
3370
3371   level->file_has_custom_elements = TRUE;
3372
3373   return chunk_size;
3374 }
3375
3376 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3377 {
3378   int num_changed_custom_elements = getFile16BitBE(file);
3379   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3380   int i, j, x, y;
3381
3382   if (chunk_size_expected != chunk_size)
3383   {
3384     ReadUnusedBytesFromFile(file, chunk_size - 2);
3385     return chunk_size_expected;
3386   }
3387
3388   for (i = 0; i < num_changed_custom_elements; i++)
3389   {
3390     int element = getMappedElement(getFile16BitBE(file));
3391     struct ElementInfo *ei = &element_info[element];
3392     unsigned int event_bits;
3393
3394     if (!IS_CUSTOM_ELEMENT(element))
3395     {
3396       Warn("invalid custom element number %d", element);
3397
3398       element = EL_INTERNAL_DUMMY;
3399     }
3400
3401     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3402       ei->description[j] = getFile8Bit(file);
3403     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3404
3405     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3406
3407     // some free bytes for future properties and padding
3408     ReadUnusedBytesFromFile(file, 7);
3409
3410     ei->use_gfx_element = getFile8Bit(file);
3411     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3412
3413     ei->collect_score_initial = getFile8Bit(file);
3414     ei->collect_count_initial = getFile8Bit(file);
3415
3416     ei->push_delay_fixed = getFile16BitBE(file);
3417     ei->push_delay_random = getFile16BitBE(file);
3418     ei->move_delay_fixed = getFile16BitBE(file);
3419     ei->move_delay_random = getFile16BitBE(file);
3420
3421     ei->move_pattern = getFile16BitBE(file);
3422     ei->move_direction_initial = getFile8Bit(file);
3423     ei->move_stepsize = getFile8Bit(file);
3424
3425     for (y = 0; y < 3; y++)
3426       for (x = 0; x < 3; x++)
3427         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3428
3429     // bits 0 - 31 of "has_event[]"
3430     event_bits = getFile32BitBE(file);
3431     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3432       if (event_bits & (1u << j))
3433         ei->change->has_event[j] = TRUE;
3434
3435     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3436
3437     ei->change->delay_fixed = getFile16BitBE(file);
3438     ei->change->delay_random = getFile16BitBE(file);
3439     ei->change->delay_frames = getFile16BitBE(file);
3440
3441     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3442
3443     ei->change->explode = getFile8Bit(file);
3444     ei->change->use_target_content = getFile8Bit(file);
3445     ei->change->only_if_complete = getFile8Bit(file);
3446     ei->change->use_random_replace = getFile8Bit(file);
3447
3448     ei->change->random_percentage = getFile8Bit(file);
3449     ei->change->replace_when = getFile8Bit(file);
3450
3451     for (y = 0; y < 3; y++)
3452       for (x = 0; x < 3; x++)
3453         ei->change->target_content.e[x][y] =
3454           getMappedElement(getFile16BitBE(file));
3455
3456     ei->slippery_type = getFile8Bit(file);
3457
3458     // some free bytes for future properties and padding
3459     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3460
3461     // mark that this custom element has been modified
3462     ei->modified_settings = TRUE;
3463   }
3464
3465   level->file_has_custom_elements = TRUE;
3466
3467   return chunk_size;
3468 }
3469
3470 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3471 {
3472   struct ElementInfo *ei;
3473   int chunk_size_expected;
3474   int element;
3475   int i, j, x, y;
3476
3477   // ---------- custom element base property values (96 bytes) ----------------
3478
3479   element = getMappedElement(getFile16BitBE(file));
3480
3481   if (!IS_CUSTOM_ELEMENT(element))
3482   {
3483     Warn("invalid custom element number %d", element);
3484
3485     ReadUnusedBytesFromFile(file, chunk_size - 2);
3486
3487     return chunk_size;
3488   }
3489
3490   ei = &element_info[element];
3491
3492   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3493     ei->description[i] = getFile8Bit(file);
3494   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3495
3496   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3497
3498   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3499
3500   ei->num_change_pages = getFile8Bit(file);
3501
3502   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3503   if (chunk_size_expected != chunk_size)
3504   {
3505     ReadUnusedBytesFromFile(file, chunk_size - 43);
3506     return chunk_size_expected;
3507   }
3508
3509   ei->ce_value_fixed_initial = getFile16BitBE(file);
3510   ei->ce_value_random_initial = getFile16BitBE(file);
3511   ei->use_last_ce_value = getFile8Bit(file);
3512
3513   ei->use_gfx_element = getFile8Bit(file);
3514   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3515
3516   ei->collect_score_initial = getFile8Bit(file);
3517   ei->collect_count_initial = getFile8Bit(file);
3518
3519   ei->drop_delay_fixed = getFile8Bit(file);
3520   ei->push_delay_fixed = getFile8Bit(file);
3521   ei->drop_delay_random = getFile8Bit(file);
3522   ei->push_delay_random = getFile8Bit(file);
3523   ei->move_delay_fixed = getFile16BitBE(file);
3524   ei->move_delay_random = getFile16BitBE(file);
3525
3526   // bits 0 - 15 of "move_pattern" ...
3527   ei->move_pattern = getFile16BitBE(file);
3528   ei->move_direction_initial = getFile8Bit(file);
3529   ei->move_stepsize = getFile8Bit(file);
3530
3531   ei->slippery_type = getFile8Bit(file);
3532
3533   for (y = 0; y < 3; y++)
3534     for (x = 0; x < 3; x++)
3535       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3536
3537   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3538   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3539   ei->move_leave_type = getFile8Bit(file);
3540
3541   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3542   ei->move_pattern |= (getFile16BitBE(file) << 16);
3543
3544   ei->access_direction = getFile8Bit(file);
3545
3546   ei->explosion_delay = getFile8Bit(file);
3547   ei->ignition_delay = getFile8Bit(file);
3548   ei->explosion_type = getFile8Bit(file);
3549
3550   // some free bytes for future custom property values and padding
3551   ReadUnusedBytesFromFile(file, 1);
3552
3553   // ---------- change page property values (48 bytes) ------------------------
3554
3555   setElementChangePages(ei, ei->num_change_pages);
3556
3557   for (i = 0; i < ei->num_change_pages; i++)
3558   {
3559     struct ElementChangeInfo *change = &ei->change_page[i];
3560     unsigned int event_bits;
3561
3562     // always start with reliable default values
3563     setElementChangeInfoToDefaults(change);
3564
3565     // bits 0 - 31 of "has_event[]" ...
3566     event_bits = getFile32BitBE(file);
3567     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3568       if (event_bits & (1u << j))
3569         change->has_event[j] = TRUE;
3570
3571     change->target_element = getMappedElement(getFile16BitBE(file));
3572
3573     change->delay_fixed = getFile16BitBE(file);
3574     change->delay_random = getFile16BitBE(file);
3575     change->delay_frames = getFile16BitBE(file);
3576
3577     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3578
3579     change->explode = getFile8Bit(file);
3580     change->use_target_content = getFile8Bit(file);
3581     change->only_if_complete = getFile8Bit(file);
3582     change->use_random_replace = getFile8Bit(file);
3583
3584     change->random_percentage = getFile8Bit(file);
3585     change->replace_when = getFile8Bit(file);
3586
3587     for (y = 0; y < 3; y++)
3588       for (x = 0; x < 3; x++)
3589         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3590
3591     change->can_change = getFile8Bit(file);
3592
3593     change->trigger_side = getFile8Bit(file);
3594
3595     change->trigger_player = getFile8Bit(file);
3596     change->trigger_page = getFile8Bit(file);
3597
3598     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3599                             CH_PAGE_ANY : (1 << change->trigger_page));
3600
3601     change->has_action = getFile8Bit(file);
3602     change->action_type = getFile8Bit(file);
3603     change->action_mode = getFile8Bit(file);
3604     change->action_arg = getFile16BitBE(file);
3605
3606     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3607     event_bits = getFile8Bit(file);
3608     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3609       if (event_bits & (1u << (j - 32)))
3610         change->has_event[j] = TRUE;
3611   }
3612
3613   // mark this custom element as modified
3614   ei->modified_settings = TRUE;
3615
3616   level->file_has_custom_elements = TRUE;
3617
3618   return chunk_size;
3619 }
3620
3621 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3622 {
3623   struct ElementInfo *ei;
3624   struct ElementGroupInfo *group;
3625   int element;
3626   int i;
3627
3628   element = getMappedElement(getFile16BitBE(file));
3629
3630   if (!IS_GROUP_ELEMENT(element))
3631   {
3632     Warn("invalid group element number %d", element);
3633
3634     ReadUnusedBytesFromFile(file, chunk_size - 2);
3635
3636     return chunk_size;
3637   }
3638
3639   ei = &element_info[element];
3640
3641   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3642     ei->description[i] = getFile8Bit(file);
3643   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3644
3645   group = element_info[element].group;
3646
3647   group->num_elements = getFile8Bit(file);
3648
3649   ei->use_gfx_element = getFile8Bit(file);
3650   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3651
3652   group->choice_mode = getFile8Bit(file);
3653
3654   // some free bytes for future values and padding
3655   ReadUnusedBytesFromFile(file, 3);
3656
3657   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3658     group->element[i] = getMappedElement(getFile16BitBE(file));
3659
3660   // mark this group element as modified
3661   element_info[element].modified_settings = TRUE;
3662
3663   level->file_has_custom_elements = TRUE;
3664
3665   return chunk_size;
3666 }
3667
3668 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3669                                 int element, int real_element)
3670 {
3671   int micro_chunk_size = 0;
3672   int conf_type = getFile8Bit(file);
3673   int byte_mask = conf_type & CONF_MASK_BYTES;
3674   boolean element_found = FALSE;
3675   int i;
3676
3677   micro_chunk_size += 1;
3678
3679   if (byte_mask == CONF_MASK_MULTI_BYTES)
3680   {
3681     int num_bytes = getFile16BitBE(file);
3682     byte *buffer = checked_malloc(num_bytes);
3683
3684     ReadBytesFromFile(file, buffer, num_bytes);
3685
3686     for (i = 0; conf[i].data_type != -1; i++)
3687     {
3688       if (conf[i].element == element &&
3689           conf[i].conf_type == conf_type)
3690       {
3691         int data_type = conf[i].data_type;
3692         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3693         int max_num_entities = conf[i].max_num_entities;
3694
3695         if (num_entities > max_num_entities)
3696         {
3697           Warn("truncating number of entities for element %d from %d to %d",
3698                element, num_entities, max_num_entities);
3699
3700           num_entities = max_num_entities;
3701         }
3702
3703         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3704                                   data_type == TYPE_CONTENT_LIST))
3705         {
3706           // for element and content lists, zero entities are not allowed
3707           Warn("found empty list of entities for element %d", element);
3708
3709           // do not set "num_entities" here to prevent reading behind buffer
3710
3711           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3712         }
3713         else
3714         {
3715           *(int *)(conf[i].num_entities) = num_entities;
3716         }
3717
3718         element_found = TRUE;
3719
3720         if (data_type == TYPE_STRING)
3721         {
3722           char *string = (char *)(conf[i].value);
3723           int j;
3724
3725           for (j = 0; j < max_num_entities; j++)
3726             string[j] = (j < num_entities ? buffer[j] : '\0');
3727         }
3728         else if (data_type == TYPE_ELEMENT_LIST)
3729         {
3730           int *element_array = (int *)(conf[i].value);
3731           int j;
3732
3733           for (j = 0; j < num_entities; j++)
3734             element_array[j] =
3735               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3736         }
3737         else if (data_type == TYPE_CONTENT_LIST)
3738         {
3739           struct Content *content= (struct Content *)(conf[i].value);
3740           int c, x, y;
3741
3742           for (c = 0; c < num_entities; c++)
3743             for (y = 0; y < 3; y++)
3744               for (x = 0; x < 3; x++)
3745                 content[c].e[x][y] =
3746                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3747         }
3748         else
3749           element_found = FALSE;
3750
3751         break;
3752       }
3753     }
3754
3755     checked_free(buffer);
3756
3757     micro_chunk_size += 2 + num_bytes;
3758   }
3759   else          // constant size configuration data (1, 2 or 4 bytes)
3760   {
3761     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3762                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3763                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3764
3765     for (i = 0; conf[i].data_type != -1; i++)
3766     {
3767       if (conf[i].element == element &&
3768           conf[i].conf_type == conf_type)
3769       {
3770         int data_type = conf[i].data_type;
3771
3772         if (data_type == TYPE_ELEMENT)
3773           value = getMappedElement(value);
3774
3775         if (data_type == TYPE_BOOLEAN)
3776           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3777         else
3778           *(int *)    (conf[i].value) = value;
3779
3780         element_found = TRUE;
3781
3782         break;
3783       }
3784     }
3785
3786     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3787   }
3788
3789   if (!element_found)
3790   {
3791     char *error_conf_chunk_bytes =
3792       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3793        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3794        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3795     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3796     int error_element = real_element;
3797
3798     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3799          error_conf_chunk_bytes, error_conf_chunk_token,
3800          error_element, EL_NAME(error_element));
3801   }
3802
3803   return micro_chunk_size;
3804 }
3805
3806 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3807 {
3808   int real_chunk_size = 0;
3809
3810   li = *level;          // copy level data into temporary buffer
3811
3812   while (!checkEndOfFile(file))
3813   {
3814     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3815
3816     if (real_chunk_size >= chunk_size)
3817       break;
3818   }
3819
3820   *level = li;          // copy temporary buffer back to level data
3821
3822   return real_chunk_size;
3823 }
3824
3825 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3826 {
3827   int real_chunk_size = 0;
3828
3829   li = *level;          // copy level data into temporary buffer
3830
3831   while (!checkEndOfFile(file))
3832   {
3833     int element = getMappedElement(getFile16BitBE(file));
3834
3835     real_chunk_size += 2;
3836     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3837                                             element, element);
3838     if (real_chunk_size >= chunk_size)
3839       break;
3840   }
3841
3842   *level = li;          // copy temporary buffer back to level data
3843
3844   return real_chunk_size;
3845 }
3846
3847 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3848 {
3849   int real_chunk_size = 0;
3850
3851   li = *level;          // copy level data into temporary buffer
3852
3853   while (!checkEndOfFile(file))
3854   {
3855     int element = getMappedElement(getFile16BitBE(file));
3856
3857     real_chunk_size += 2;
3858     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3859                                             element, element);
3860     if (real_chunk_size >= chunk_size)
3861       break;
3862   }
3863
3864   *level = li;          // copy temporary buffer back to level data
3865
3866   return real_chunk_size;
3867 }
3868
3869 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3870 {
3871   int element = getMappedElement(getFile16BitBE(file));
3872   int envelope_nr = element - EL_ENVELOPE_1;
3873   int real_chunk_size = 2;
3874
3875   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3876
3877   while (!checkEndOfFile(file))
3878   {
3879     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3880                                             -1, element);
3881
3882     if (real_chunk_size >= chunk_size)
3883       break;
3884   }
3885
3886   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3887
3888   return real_chunk_size;
3889 }
3890
3891 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3892 {
3893   int element = getMappedElement(getFile16BitBE(file));
3894   int real_chunk_size = 2;
3895   struct ElementInfo *ei = &element_info[element];
3896   int i;
3897
3898   xx_ei = *ei;          // copy element data into temporary buffer
3899
3900   xx_ei.num_change_pages = -1;
3901
3902   while (!checkEndOfFile(file))
3903   {
3904     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3905                                             -1, element);
3906     if (xx_ei.num_change_pages != -1)
3907       break;
3908
3909     if (real_chunk_size >= chunk_size)
3910       break;
3911   }
3912
3913   *ei = xx_ei;
3914
3915   if (ei->num_change_pages == -1)
3916   {
3917     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3918          EL_NAME(element));
3919
3920     ei->num_change_pages = 1;
3921
3922     setElementChangePages(ei, 1);
3923     setElementChangeInfoToDefaults(ei->change);
3924
3925     return real_chunk_size;
3926   }
3927
3928   // initialize number of change pages stored for this custom element
3929   setElementChangePages(ei, ei->num_change_pages);
3930   for (i = 0; i < ei->num_change_pages; i++)
3931     setElementChangeInfoToDefaults(&ei->change_page[i]);
3932
3933   // start with reading properties for the first change page
3934   xx_current_change_page = 0;
3935
3936   while (!checkEndOfFile(file))
3937   {
3938     // level file might contain invalid change page number
3939     if (xx_current_change_page >= ei->num_change_pages)
3940       break;
3941
3942     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3943
3944     xx_change = *change;        // copy change data into temporary buffer
3945
3946     resetEventBits();           // reset bits; change page might have changed
3947
3948     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3949                                             -1, element);
3950
3951     *change = xx_change;
3952
3953     setEventFlagsFromEventBits(change);
3954
3955     if (real_chunk_size >= chunk_size)
3956       break;
3957   }
3958
3959   level->file_has_custom_elements = TRUE;
3960
3961   return real_chunk_size;
3962 }
3963
3964 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3965 {
3966   int element = getMappedElement(getFile16BitBE(file));
3967   int real_chunk_size = 2;
3968   struct ElementInfo *ei = &element_info[element];
3969   struct ElementGroupInfo *group = ei->group;
3970
3971   if (group == NULL)
3972     return -1;
3973
3974   xx_ei = *ei;          // copy element data into temporary buffer
3975   xx_group = *group;    // copy group data into temporary buffer
3976
3977   while (!checkEndOfFile(file))
3978   {
3979     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3980                                             -1, element);
3981
3982     if (real_chunk_size >= chunk_size)
3983       break;
3984   }
3985
3986   *ei = xx_ei;
3987   *group = xx_group;
3988
3989   level->file_has_custom_elements = TRUE;
3990
3991   return real_chunk_size;
3992 }
3993
3994 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3995 {
3996   int element = getMappedElement(getFile16BitBE(file));
3997   int real_chunk_size = 2;
3998   struct ElementInfo *ei = &element_info[element];
3999
4000   xx_ei = *ei;          // copy element data into temporary buffer
4001
4002   while (!checkEndOfFile(file))
4003   {
4004     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4005                                             -1, element);
4006
4007     if (real_chunk_size >= chunk_size)
4008       break;
4009   }
4010
4011   *ei = xx_ei;
4012
4013   level->file_has_custom_elements = TRUE;
4014
4015   return real_chunk_size;
4016 }
4017
4018 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4019                                       struct LevelFileInfo *level_file_info,
4020                                       boolean level_info_only)
4021 {
4022   char *filename = level_file_info->filename;
4023   char cookie[MAX_LINE_LEN];
4024   char chunk_name[CHUNK_ID_LEN + 1];
4025   int chunk_size;
4026   File *file;
4027
4028   if (!(file = openFile(filename, MODE_READ)))
4029   {
4030     level->no_valid_file = TRUE;
4031     level->no_level_file = TRUE;
4032
4033     if (level_info_only)
4034       return;
4035
4036     Warn("cannot read level '%s' -- using empty level", filename);
4037
4038     if (!setup.editor.use_template_for_new_levels)
4039       return;
4040
4041     // if level file not found, try to initialize level data from template
4042     filename = getGlobalLevelTemplateFilename();
4043
4044     if (!(file = openFile(filename, MODE_READ)))
4045       return;
4046
4047     // default: for empty levels, use level template for custom elements
4048     level->use_custom_template = TRUE;
4049
4050     level->no_valid_file = FALSE;
4051   }
4052
4053   getFileChunkBE(file, chunk_name, NULL);
4054   if (strEqual(chunk_name, "RND1"))
4055   {
4056     getFile32BitBE(file);               // not used
4057
4058     getFileChunkBE(file, chunk_name, NULL);
4059     if (!strEqual(chunk_name, "CAVE"))
4060     {
4061       level->no_valid_file = TRUE;
4062
4063       Warn("unknown format of level file '%s'", filename);
4064
4065       closeFile(file);
4066
4067       return;
4068     }
4069   }
4070   else  // check for pre-2.0 file format with cookie string
4071   {
4072     strcpy(cookie, chunk_name);
4073     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4074       cookie[4] = '\0';
4075     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4076       cookie[strlen(cookie) - 1] = '\0';
4077
4078     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4079     {
4080       level->no_valid_file = TRUE;
4081
4082       Warn("unknown format of level file '%s'", filename);
4083
4084       closeFile(file);
4085
4086       return;
4087     }
4088
4089     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4090     {
4091       level->no_valid_file = TRUE;
4092
4093       Warn("unsupported version of level file '%s'", filename);
4094
4095       closeFile(file);
4096
4097       return;
4098     }
4099
4100     // pre-2.0 level files have no game version, so use file version here
4101     level->game_version = level->file_version;
4102   }
4103
4104   if (level->file_version < FILE_VERSION_1_2)
4105   {
4106     // level files from versions before 1.2.0 without chunk structure
4107     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4108     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4109   }
4110   else
4111   {
4112     static struct
4113     {
4114       char *name;
4115       int size;
4116       int (*loader)(File *, int, struct LevelInfo *);
4117     }
4118     chunk_info[] =
4119     {
4120       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4121       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4122       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4123       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4124       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4125       { "INFO", -1,                     LoadLevel_INFO },
4126       { "BODY", -1,                     LoadLevel_BODY },
4127       { "CONT", -1,                     LoadLevel_CONT },
4128       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4129       { "CNT3", -1,                     LoadLevel_CNT3 },
4130       { "CUS1", -1,                     LoadLevel_CUS1 },
4131       { "CUS2", -1,                     LoadLevel_CUS2 },
4132       { "CUS3", -1,                     LoadLevel_CUS3 },
4133       { "CUS4", -1,                     LoadLevel_CUS4 },
4134       { "GRP1", -1,                     LoadLevel_GRP1 },
4135       { "CONF", -1,                     LoadLevel_CONF },
4136       { "ELEM", -1,                     LoadLevel_ELEM },
4137       { "NOTE", -1,                     LoadLevel_NOTE },
4138       { "CUSX", -1,                     LoadLevel_CUSX },
4139       { "GRPX", -1,                     LoadLevel_GRPX },
4140       { "EMPX", -1,                     LoadLevel_EMPX },
4141
4142       {  NULL,  0,                      NULL }
4143     };
4144
4145     while (getFileChunkBE(file, chunk_name, &chunk_size))
4146     {
4147       int i = 0;
4148
4149       while (chunk_info[i].name != NULL &&
4150              !strEqual(chunk_name, chunk_info[i].name))
4151         i++;
4152
4153       if (chunk_info[i].name == NULL)
4154       {
4155         Warn("unknown chunk '%s' in level file '%s'",
4156              chunk_name, filename);
4157
4158         ReadUnusedBytesFromFile(file, chunk_size);
4159       }
4160       else if (chunk_info[i].size != -1 &&
4161                chunk_info[i].size != chunk_size)
4162       {
4163         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4164              chunk_size, chunk_name, filename);
4165
4166         ReadUnusedBytesFromFile(file, chunk_size);
4167       }
4168       else
4169       {
4170         // call function to load this level chunk
4171         int chunk_size_expected =
4172           (chunk_info[i].loader)(file, chunk_size, level);
4173
4174         if (chunk_size_expected < 0)
4175         {
4176           Warn("error reading chunk '%s' in level file '%s'",
4177                chunk_name, filename);
4178
4179           break;
4180         }
4181
4182         // the size of some chunks cannot be checked before reading other
4183         // chunks first (like "HEAD" and "BODY") that contain some header
4184         // information, so check them here
4185         if (chunk_size_expected != chunk_size)
4186         {
4187           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4188                chunk_size, chunk_name, filename);
4189
4190           break;
4191         }
4192       }
4193     }
4194   }
4195
4196   closeFile(file);
4197 }
4198
4199
4200 // ----------------------------------------------------------------------------
4201 // functions for loading BD level
4202 // ----------------------------------------------------------------------------
4203
4204 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4205 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4206
4207 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4208 {
4209   struct LevelInfo_BD *level_bd = level->native_bd_level;
4210   GdCave *cave = NULL;  // will be changed below
4211   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4212   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4213   int x, y;
4214
4215   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4216
4217   // cave and map newly allocated when set to defaults above
4218   cave = level_bd->cave;
4219
4220   // level type
4221   cave->intermission                    = level->bd_intermission;
4222
4223   // level settings
4224   cave->level_time[0]                   = level->time;
4225   cave->level_diamonds[0]               = level->gems_needed;
4226
4227   // game timing
4228   cave->scheduling                      = level->bd_scheduling_type;
4229   cave->pal_timing                      = level->bd_pal_timing;
4230   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4231   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4232   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4233   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4234
4235   // scores
4236   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4237   cave->diamond_value                   = level->score[SC_EMERALD];
4238   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4239
4240   // compatibility settings
4241   cave->lineshift                       = level->bd_line_shifting_borders;
4242   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4243   cave->short_explosions                = level->bd_short_explosions;
4244
4245   // player properties
4246   cave->diagonal_movements              = level->bd_diagonal_movements;
4247   cave->active_is_first_found           = level->bd_topmost_player_active;
4248   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4249   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4250   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4251   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4252
4253   // element properties
4254   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4255   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4256   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4257   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4258   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4259   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4260   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4261   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4262   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4263
4264   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4265   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4266   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4267   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4268   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4269   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4270   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4271
4272   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4273   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4274   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4275   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4276   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4277   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4278   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4279   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4280   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4281   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4282   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4283
4284   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4285   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4286   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4287   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4288   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4289   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4290
4291   cave->slime_predictable               = level->bd_slime_is_predictable;
4292   cave->slime_correct_random            = level->bd_slime_correct_random;
4293   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4294   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4295   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4296   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4297   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4298   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4299   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4300   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4301   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4302   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4303
4304   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4305   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4306   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4307
4308   cave->biter_delay_frame               = level->bd_biter_move_delay;
4309   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4310
4311   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4312
4313   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4314
4315   cave->replicators_active              = level->bd_replicators_active;
4316   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4317
4318   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4319   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4320
4321   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4322
4323   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4324
4325   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4326   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4327   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4328
4329   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4330   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4331
4332   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4333   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4334
4335   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4336   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4337   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4338
4339   cave->gravity                         = level->bd_gravity_direction;
4340   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4341   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4342   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4343
4344   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4345   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4346   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4347   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4348
4349   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4350   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4351   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4352   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4353   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4354   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4355
4356   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4357   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4358   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4359   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4360
4361   // level name
4362   strncpy(cave->name, level->name, sizeof(GdString));
4363   cave->name[sizeof(GdString) - 1] = '\0';
4364
4365   // playfield elements
4366   for (x = 0; x < cave->w; x++)
4367     for (y = 0; y < cave->h; y++)
4368       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4369 }
4370
4371 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4372 {
4373   struct LevelInfo_BD *level_bd = level->native_bd_level;
4374   GdCave *cave = level_bd->cave;
4375   int bd_level_nr = level_bd->level_nr;
4376   int x, y;
4377
4378   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4379   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4380
4381   // level type
4382   level->bd_intermission                = cave->intermission;
4383
4384   // level settings
4385   level->time                           = cave->level_time[bd_level_nr];
4386   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4387
4388   // game timing
4389   level->bd_scheduling_type             = cave->scheduling;
4390   level->bd_pal_timing                  = cave->pal_timing;
4391   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4392   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4393   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4394   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4395
4396   // scores
4397   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4398   level->score[SC_EMERALD]              = cave->diamond_value;
4399   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4400
4401   // compatibility settings
4402   level->bd_line_shifting_borders       = cave->lineshift;
4403   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4404   level->bd_short_explosions            = cave->short_explosions;
4405
4406   // player properties
4407   level->bd_diagonal_movements          = cave->diagonal_movements;
4408   level->bd_topmost_player_active       = cave->active_is_first_found;
4409   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4410   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4411   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4412   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4413
4414   // element properties
4415   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4416   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4417   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4418   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4419   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4420   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4421   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4422   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4423   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4424
4425   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4426   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4427   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4428   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4429   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4430   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4431   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4432
4433   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4434   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4435   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4436   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4437   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4438   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4439   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4440   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4441   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4442   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4443   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4444
4445   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4446   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4447   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4448   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4449   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4450   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4451
4452   level->bd_slime_is_predictable        = cave->slime_predictable;
4453   level->bd_slime_correct_random        = cave->slime_correct_random;
4454   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4455   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4456   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4457   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4458   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4459   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4460   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4461   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4462   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4463   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4464
4465   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4466   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4467   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4468
4469   level->bd_biter_move_delay            = cave->biter_delay_frame;
4470   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4471
4472   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4473
4474   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4475
4476   level->bd_replicators_active          = cave->replicators_active;
4477   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4478
4479   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4480   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4481
4482   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4483
4484   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4485
4486   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4487   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4488   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4489
4490   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4491   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4492
4493   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4494   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4495
4496   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4497   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4498   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4499
4500   level->bd_gravity_direction           = cave->gravity;
4501   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4502   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4503   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4504
4505   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4506   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4507   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4508   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4509
4510   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4511   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4512   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4513   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4514   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4515   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4516
4517   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4518   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4519   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4520   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4521
4522   // level name
4523   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4524
4525   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4526   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4527
4528   // playfield elements
4529   for (x = 0; x < level->fieldx; x++)
4530     for (y = 0; y < level->fieldy; y++)
4531       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4532
4533   checked_free(cave_name);
4534 }
4535
4536 static void setTapeInfoToDefaults(void);
4537
4538 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4539 {
4540   struct LevelInfo_BD *level_bd = level->native_bd_level;
4541   GdCave *cave = level_bd->cave;
4542   GdReplay *replay = level_bd->replay;
4543   int i;
4544
4545   if (replay == NULL)
4546     return;
4547
4548   // always start with reliable default values
4549   setTapeInfoToDefaults();
4550
4551   tape.level_nr = level_nr;             // (currently not used)
4552   tape.random_seed = replay->seed;
4553
4554   TapeSetDateFromIsoDateString(replay->date);
4555
4556   tape.counter = 0;
4557   tape.pos[tape.counter].delay = 0;
4558
4559   tape.bd_replay = TRUE;
4560
4561   // all time calculations only used to display approximate tape time
4562   int cave_speed = cave->speed;
4563   int milliseconds_game = 0;
4564   int milliseconds_elapsed = 20;
4565
4566   for (i = 0; i < replay->movements->len; i++)
4567   {
4568     int replay_action = replay->movements->data[i];
4569     int tape_action = map_action_BD_to_RND(replay_action);
4570     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4571     boolean success = 0;
4572
4573     while (1)
4574     {
4575       success = TapeAddAction(action);
4576
4577       milliseconds_game += milliseconds_elapsed;
4578
4579       if (milliseconds_game >= cave_speed)
4580       {
4581         milliseconds_game -= cave_speed;
4582
4583         break;
4584       }
4585     }
4586
4587     tape.counter++;
4588     tape.pos[tape.counter].delay = 0;
4589     tape.pos[tape.counter].action[0] = 0;
4590
4591     if (!success)
4592     {
4593       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4594
4595       break;
4596     }
4597   }
4598
4599   TapeHaltRecording();
4600 }
4601
4602
4603 // ----------------------------------------------------------------------------
4604 // functions for loading EM level
4605 // ----------------------------------------------------------------------------
4606
4607 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4608 {
4609   static int ball_xy[8][2] =
4610   {
4611     { 0, 0 },
4612     { 1, 0 },
4613     { 2, 0 },
4614     { 0, 1 },
4615     { 2, 1 },
4616     { 0, 2 },
4617     { 1, 2 },
4618     { 2, 2 },
4619   };
4620   struct LevelInfo_EM *level_em = level->native_em_level;
4621   struct CAVE *cav = level_em->cav;
4622   int i, j, x, y;
4623
4624   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4625   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4626
4627   cav->time_seconds     = level->time;
4628   cav->gems_needed      = level->gems_needed;
4629
4630   cav->emerald_score    = level->score[SC_EMERALD];
4631   cav->diamond_score    = level->score[SC_DIAMOND];
4632   cav->alien_score      = level->score[SC_ROBOT];
4633   cav->tank_score       = level->score[SC_SPACESHIP];
4634   cav->bug_score        = level->score[SC_BUG];
4635   cav->eater_score      = level->score[SC_YAMYAM];
4636   cav->nut_score        = level->score[SC_NUT];
4637   cav->dynamite_score   = level->score[SC_DYNAMITE];
4638   cav->key_score        = level->score[SC_KEY];
4639   cav->exit_score       = level->score[SC_TIME_BONUS];
4640
4641   cav->num_eater_arrays = level->num_yamyam_contents;
4642
4643   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4644     for (y = 0; y < 3; y++)
4645       for (x = 0; x < 3; x++)
4646         cav->eater_array[i][y * 3 + x] =
4647           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4648
4649   cav->amoeba_time              = level->amoeba_speed;
4650   cav->wonderwall_time          = level->time_magic_wall;
4651   cav->wheel_time               = level->time_wheel;
4652
4653   cav->android_move_time        = level->android_move_time;
4654   cav->android_clone_time       = level->android_clone_time;
4655   cav->ball_random              = level->ball_random;
4656   cav->ball_active              = level->ball_active_initial;
4657   cav->ball_time                = level->ball_time;
4658   cav->num_ball_arrays          = level->num_ball_contents;
4659
4660   cav->lenses_score             = level->lenses_score;
4661   cav->magnify_score            = level->magnify_score;
4662   cav->slurp_score              = level->slurp_score;
4663
4664   cav->lenses_time              = level->lenses_time;
4665   cav->magnify_time             = level->magnify_time;
4666
4667   cav->wind_time = 9999;
4668   cav->wind_direction =
4669     map_direction_RND_to_EM(level->wind_direction_initial);
4670
4671   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4672     for (j = 0; j < 8; j++)
4673       cav->ball_array[i][j] =
4674         map_element_RND_to_EM_cave(level->ball_content[i].
4675                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4676
4677   map_android_clone_elements_RND_to_EM(level);
4678
4679   // first fill the complete playfield with the empty space element
4680   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4681     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4682       cav->cave[x][y] = Cblank;
4683
4684   // then copy the real level contents from level file into the playfield
4685   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4686   {
4687     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4688
4689     if (level->field[x][y] == EL_AMOEBA_DEAD)
4690       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4691
4692     cav->cave[x][y] = new_element;
4693   }
4694
4695   for (i = 0; i < MAX_PLAYERS; i++)
4696   {
4697     cav->player_x[i] = -1;
4698     cav->player_y[i] = -1;
4699   }
4700
4701   // initialize player positions and delete players from the playfield
4702   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4703   {
4704     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4705     {
4706       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4707
4708       cav->player_x[player_nr] = x;
4709       cav->player_y[player_nr] = y;
4710
4711       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4712     }
4713   }
4714 }
4715
4716 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4717 {
4718   static int ball_xy[8][2] =
4719   {
4720     { 0, 0 },
4721     { 1, 0 },
4722     { 2, 0 },
4723     { 0, 1 },
4724     { 2, 1 },
4725     { 0, 2 },
4726     { 1, 2 },
4727     { 2, 2 },
4728   };
4729   struct LevelInfo_EM *level_em = level->native_em_level;
4730   struct CAVE *cav = level_em->cav;
4731   int i, j, x, y;
4732
4733   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4734   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4735
4736   level->time        = cav->time_seconds;
4737   level->gems_needed = cav->gems_needed;
4738
4739   sprintf(level->name, "Level %d", level->file_info.nr);
4740
4741   level->score[SC_EMERALD]      = cav->emerald_score;
4742   level->score[SC_DIAMOND]      = cav->diamond_score;
4743   level->score[SC_ROBOT]        = cav->alien_score;
4744   level->score[SC_SPACESHIP]    = cav->tank_score;
4745   level->score[SC_BUG]          = cav->bug_score;
4746   level->score[SC_YAMYAM]       = cav->eater_score;
4747   level->score[SC_NUT]          = cav->nut_score;
4748   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4749   level->score[SC_KEY]          = cav->key_score;
4750   level->score[SC_TIME_BONUS]   = cav->exit_score;
4751
4752   level->num_yamyam_contents    = cav->num_eater_arrays;
4753
4754   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4755     for (y = 0; y < 3; y++)
4756       for (x = 0; x < 3; x++)
4757         level->yamyam_content[i].e[x][y] =
4758           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4759
4760   level->amoeba_speed           = cav->amoeba_time;
4761   level->time_magic_wall        = cav->wonderwall_time;
4762   level->time_wheel             = cav->wheel_time;
4763
4764   level->android_move_time      = cav->android_move_time;
4765   level->android_clone_time     = cav->android_clone_time;
4766   level->ball_random            = cav->ball_random;
4767   level->ball_active_initial    = cav->ball_active;
4768   level->ball_time              = cav->ball_time;
4769   level->num_ball_contents      = cav->num_ball_arrays;
4770
4771   level->lenses_score           = cav->lenses_score;
4772   level->magnify_score          = cav->magnify_score;
4773   level->slurp_score            = cav->slurp_score;
4774
4775   level->lenses_time            = cav->lenses_time;
4776   level->magnify_time           = cav->magnify_time;
4777
4778   level->wind_direction_initial =
4779     map_direction_EM_to_RND(cav->wind_direction);
4780
4781   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4782     for (j = 0; j < 8; j++)
4783       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4784         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4785
4786   map_android_clone_elements_EM_to_RND(level);
4787
4788   // convert the playfield (some elements need special treatment)
4789   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4790   {
4791     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4792
4793     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4794       new_element = EL_AMOEBA_DEAD;
4795
4796     level->field[x][y] = new_element;
4797   }
4798
4799   for (i = 0; i < MAX_PLAYERS; i++)
4800   {
4801     // in case of all players set to the same field, use the first player
4802     int nr = MAX_PLAYERS - i - 1;
4803     int jx = cav->player_x[nr];
4804     int jy = cav->player_y[nr];
4805
4806     if (jx != -1 && jy != -1)
4807       level->field[jx][jy] = EL_PLAYER_1 + nr;
4808   }
4809
4810   // time score is counted for each 10 seconds left in Emerald Mine levels
4811   level->time_score_base = 10;
4812 }
4813
4814
4815 // ----------------------------------------------------------------------------
4816 // functions for loading SP level
4817 // ----------------------------------------------------------------------------
4818
4819 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4820 {
4821   struct LevelInfo_SP *level_sp = level->native_sp_level;
4822   LevelInfoType *header = &level_sp->header;
4823   int i, x, y;
4824
4825   level_sp->width  = level->fieldx;
4826   level_sp->height = level->fieldy;
4827
4828   for (x = 0; x < level->fieldx; x++)
4829     for (y = 0; y < level->fieldy; y++)
4830       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4831
4832   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4833
4834   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4835     header->LevelTitle[i] = level->name[i];
4836   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4837
4838   header->InfotronsNeeded = level->gems_needed;
4839
4840   header->SpecialPortCount = 0;
4841
4842   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4843   {
4844     boolean gravity_port_found = FALSE;
4845     boolean gravity_port_valid = FALSE;
4846     int gravity_port_flag;
4847     int gravity_port_base_element;
4848     int element = level->field[x][y];
4849
4850     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4851         element <= EL_SP_GRAVITY_ON_PORT_UP)
4852     {
4853       gravity_port_found = TRUE;
4854       gravity_port_valid = TRUE;
4855       gravity_port_flag = 1;
4856       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4857     }
4858     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4859              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4860     {
4861       gravity_port_found = TRUE;
4862       gravity_port_valid = TRUE;
4863       gravity_port_flag = 0;
4864       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4865     }
4866     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4867              element <= EL_SP_GRAVITY_PORT_UP)
4868     {
4869       // change R'n'D style gravity inverting special port to normal port
4870       // (there are no gravity inverting ports in native Supaplex engine)
4871
4872       gravity_port_found = TRUE;
4873       gravity_port_valid = FALSE;
4874       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4875     }
4876
4877     if (gravity_port_found)
4878     {
4879       if (gravity_port_valid &&
4880           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4881       {
4882         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4883
4884         port->PortLocation = (y * level->fieldx + x) * 2;
4885         port->Gravity = gravity_port_flag;
4886
4887         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4888
4889         header->SpecialPortCount++;
4890       }
4891       else
4892       {
4893         // change special gravity port to normal port
4894
4895         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4896       }
4897
4898       level_sp->playfield[x][y] = element - EL_SP_START;
4899     }
4900   }
4901 }
4902
4903 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4904 {
4905   struct LevelInfo_SP *level_sp = level->native_sp_level;
4906   LevelInfoType *header = &level_sp->header;
4907   boolean num_invalid_elements = 0;
4908   int i, j, x, y;
4909
4910   level->fieldx = level_sp->width;
4911   level->fieldy = level_sp->height;
4912
4913   for (x = 0; x < level->fieldx; x++)
4914   {
4915     for (y = 0; y < level->fieldy; y++)
4916     {
4917       int element_old = level_sp->playfield[x][y];
4918       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4919
4920       if (element_new == EL_UNKNOWN)
4921       {
4922         num_invalid_elements++;
4923
4924         Debug("level:native:SP", "invalid element %d at position %d, %d",
4925               element_old, x, y);
4926       }
4927
4928       level->field[x][y] = element_new;
4929     }
4930   }
4931
4932   if (num_invalid_elements > 0)
4933     Warn("found %d invalid elements%s", num_invalid_elements,
4934          (!options.debug ? " (use '--debug' for more details)" : ""));
4935
4936   for (i = 0; i < MAX_PLAYERS; i++)
4937     level->initial_player_gravity[i] =
4938       (header->InitialGravity == 1 ? TRUE : FALSE);
4939
4940   // skip leading spaces
4941   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4942     if (header->LevelTitle[i] != ' ')
4943       break;
4944
4945   // copy level title
4946   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4947     level->name[j] = header->LevelTitle[i];
4948   level->name[j] = '\0';
4949
4950   // cut trailing spaces
4951   for (; j > 0; j--)
4952     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4953       level->name[j - 1] = '\0';
4954
4955   level->gems_needed = header->InfotronsNeeded;
4956
4957   for (i = 0; i < header->SpecialPortCount; i++)
4958   {
4959     SpecialPortType *port = &header->SpecialPort[i];
4960     int port_location = port->PortLocation;
4961     int gravity = port->Gravity;
4962     int port_x, port_y, port_element;
4963
4964     port_x = (port_location / 2) % level->fieldx;
4965     port_y = (port_location / 2) / level->fieldx;
4966
4967     if (port_x < 0 || port_x >= level->fieldx ||
4968         port_y < 0 || port_y >= level->fieldy)
4969     {
4970       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4971
4972       continue;
4973     }
4974
4975     port_element = level->field[port_x][port_y];
4976
4977     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4978         port_element > EL_SP_GRAVITY_PORT_UP)
4979     {
4980       Warn("no special port at position (%d, %d)", port_x, port_y);
4981
4982       continue;
4983     }
4984
4985     // change previous (wrong) gravity inverting special port to either
4986     // gravity enabling special port or gravity disabling special port
4987     level->field[port_x][port_y] +=
4988       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4989        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4990   }
4991
4992   // change special gravity ports without database entries to normal ports
4993   for (x = 0; x < level->fieldx; x++)
4994     for (y = 0; y < level->fieldy; y++)
4995       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4996           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4997         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4998
4999   level->time = 0;                      // no time limit
5000   level->amoeba_speed = 0;
5001   level->time_magic_wall = 0;
5002   level->time_wheel = 0;
5003   level->amoeba_content = EL_EMPTY;
5004
5005   // original Supaplex does not use score values -- rate by playing time
5006   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5007     level->score[i] = 0;
5008
5009   level->rate_time_over_score = TRUE;
5010
5011   // there are no yamyams in supaplex levels
5012   for (i = 0; i < level->num_yamyam_contents; i++)
5013     for (x = 0; x < 3; x++)
5014       for (y = 0; y < 3; y++)
5015         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5016 }
5017
5018 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5019 {
5020   struct LevelInfo_SP *level_sp = level->native_sp_level;
5021   struct DemoInfo_SP *demo = &level_sp->demo;
5022   int i, j;
5023
5024   // always start with reliable default values
5025   demo->is_available = FALSE;
5026   demo->length = 0;
5027
5028   if (TAPE_IS_EMPTY(tape))
5029     return;
5030
5031   demo->level_nr = tape.level_nr;       // (currently not used)
5032
5033   level_sp->header.DemoRandomSeed = tape.random_seed;
5034
5035   demo->length = 0;
5036
5037   for (i = 0; i < tape.length; i++)
5038   {
5039     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5040     int demo_repeat = tape.pos[i].delay;
5041     int demo_entries = (demo_repeat + 15) / 16;
5042
5043     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5044     {
5045       Warn("tape truncated: size exceeds maximum SP demo size %d",
5046            SP_MAX_TAPE_LEN);
5047
5048       break;
5049     }
5050
5051     for (j = 0; j < demo_repeat / 16; j++)
5052       demo->data[demo->length++] = 0xf0 | demo_action;
5053
5054     if (demo_repeat % 16)
5055       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5056   }
5057
5058   demo->is_available = TRUE;
5059 }
5060
5061 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5062 {
5063   struct LevelInfo_SP *level_sp = level->native_sp_level;
5064   struct DemoInfo_SP *demo = &level_sp->demo;
5065   char *filename = level->file_info.filename;
5066   int i;
5067
5068   // always start with reliable default values
5069   setTapeInfoToDefaults();
5070
5071   if (!demo->is_available)
5072     return;
5073
5074   tape.level_nr = demo->level_nr;       // (currently not used)
5075   tape.random_seed = level_sp->header.DemoRandomSeed;
5076
5077   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5078
5079   tape.counter = 0;
5080   tape.pos[tape.counter].delay = 0;
5081
5082   for (i = 0; i < demo->length; i++)
5083   {
5084     int demo_action = demo->data[i] & 0x0f;
5085     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5086     int tape_action = map_key_SP_to_RND(demo_action);
5087     int tape_repeat = demo_repeat + 1;
5088     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5089     boolean success = 0;
5090     int j;
5091
5092     for (j = 0; j < tape_repeat; j++)
5093       success = TapeAddAction(action);
5094
5095     if (!success)
5096     {
5097       Warn("SP demo truncated: size exceeds maximum tape size %d",
5098            MAX_TAPE_LEN);
5099
5100       break;
5101     }
5102   }
5103
5104   TapeHaltRecording();
5105 }
5106
5107
5108 // ----------------------------------------------------------------------------
5109 // functions for loading MM level
5110 // ----------------------------------------------------------------------------
5111
5112 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5113 {
5114   struct LevelInfo_MM *level_mm = level->native_mm_level;
5115   int i, x, y;
5116
5117   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5118   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5119
5120   level_mm->time = level->time;
5121   level_mm->kettles_needed = level->gems_needed;
5122   level_mm->auto_count_kettles = level->auto_count_gems;
5123
5124   level_mm->mm_laser_red   = level->mm_laser_red;
5125   level_mm->mm_laser_green = level->mm_laser_green;
5126   level_mm->mm_laser_blue  = level->mm_laser_blue;
5127
5128   level_mm->df_laser_red   = level->df_laser_red;
5129   level_mm->df_laser_green = level->df_laser_green;
5130   level_mm->df_laser_blue  = level->df_laser_blue;
5131
5132   strcpy(level_mm->name, level->name);
5133   strcpy(level_mm->author, level->author);
5134
5135   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5136   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5137   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5138   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5139   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5140
5141   level_mm->amoeba_speed = level->amoeba_speed;
5142   level_mm->time_fuse    = level->mm_time_fuse;
5143   level_mm->time_bomb    = level->mm_time_bomb;
5144   level_mm->time_ball    = level->mm_time_ball;
5145   level_mm->time_block   = level->mm_time_block;
5146
5147   level_mm->num_ball_contents = level->num_mm_ball_contents;
5148   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5149   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5150   level_mm->explode_ball = level->explode_mm_ball;
5151
5152   for (i = 0; i < level->num_mm_ball_contents; i++)
5153     level_mm->ball_content[i] =
5154       map_element_RND_to_MM(level->mm_ball_content[i]);
5155
5156   for (x = 0; x < level->fieldx; x++)
5157     for (y = 0; y < level->fieldy; y++)
5158       Ur[x][y] =
5159         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5160 }
5161
5162 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5163 {
5164   struct LevelInfo_MM *level_mm = level->native_mm_level;
5165   int i, x, y;
5166
5167   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5168   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5169
5170   level->time = level_mm->time;
5171   level->gems_needed = level_mm->kettles_needed;
5172   level->auto_count_gems = level_mm->auto_count_kettles;
5173
5174   level->mm_laser_red   = level_mm->mm_laser_red;
5175   level->mm_laser_green = level_mm->mm_laser_green;
5176   level->mm_laser_blue  = level_mm->mm_laser_blue;
5177
5178   level->df_laser_red   = level_mm->df_laser_red;
5179   level->df_laser_green = level_mm->df_laser_green;
5180   level->df_laser_blue  = level_mm->df_laser_blue;
5181
5182   strcpy(level->name, level_mm->name);
5183
5184   // only overwrite author from 'levelinfo.conf' if author defined in level
5185   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5186     strcpy(level->author, level_mm->author);
5187
5188   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5189   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5190   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5191   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5192   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5193
5194   level->amoeba_speed  = level_mm->amoeba_speed;
5195   level->mm_time_fuse  = level_mm->time_fuse;
5196   level->mm_time_bomb  = level_mm->time_bomb;
5197   level->mm_time_ball  = level_mm->time_ball;
5198   level->mm_time_block = level_mm->time_block;
5199
5200   level->num_mm_ball_contents = level_mm->num_ball_contents;
5201   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5202   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5203   level->explode_mm_ball = level_mm->explode_ball;
5204
5205   for (i = 0; i < level->num_mm_ball_contents; i++)
5206     level->mm_ball_content[i] =
5207       map_element_MM_to_RND(level_mm->ball_content[i]);
5208
5209   for (x = 0; x < level->fieldx; x++)
5210     for (y = 0; y < level->fieldy; y++)
5211       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5212 }
5213
5214
5215 // ----------------------------------------------------------------------------
5216 // functions for loading DC level
5217 // ----------------------------------------------------------------------------
5218
5219 #define DC_LEVEL_HEADER_SIZE            344
5220
5221 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5222                                         boolean init)
5223 {
5224   static int last_data_encoded;
5225   static int offset1;
5226   static int offset2;
5227   int diff;
5228   int diff_hi, diff_lo;
5229   int data_hi, data_lo;
5230   unsigned short data_decoded;
5231
5232   if (init)
5233   {
5234     last_data_encoded = 0;
5235     offset1 = -1;
5236     offset2 = 0;
5237
5238     return 0;
5239   }
5240
5241   diff = data_encoded - last_data_encoded;
5242   diff_hi = diff & ~0xff;
5243   diff_lo = diff &  0xff;
5244
5245   offset2 += diff_lo;
5246
5247   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5248   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5249   data_hi = data_hi & 0xff00;
5250
5251   data_decoded = data_hi | data_lo;
5252
5253   last_data_encoded = data_encoded;
5254
5255   offset1 = (offset1 + 1) % 31;
5256   offset2 = offset2 & 0xff;
5257
5258   return data_decoded;
5259 }
5260
5261 static int getMappedElement_DC(int element)
5262 {
5263   switch (element)
5264   {
5265     case 0x0000:
5266       element = EL_ROCK;
5267       break;
5268
5269       // 0x0117 - 0x036e: (?)
5270       // EL_DIAMOND
5271
5272       // 0x042d - 0x0684: (?)
5273       // EL_EMERALD
5274
5275     case 0x06f1:
5276       element = EL_NUT;
5277       break;
5278
5279     case 0x074c:
5280       element = EL_BOMB;
5281       break;
5282
5283     case 0x07a4:
5284       element = EL_PEARL;
5285       break;
5286
5287     case 0x0823:
5288       element = EL_CRYSTAL;
5289       break;
5290
5291     case 0x0e77:        // quicksand (boulder)
5292       element = EL_QUICKSAND_FAST_FULL;
5293       break;
5294
5295     case 0x0e99:        // slow quicksand (boulder)
5296       element = EL_QUICKSAND_FULL;
5297       break;
5298
5299     case 0x0ed2:
5300       element = EL_EM_EXIT_OPEN;
5301       break;
5302
5303     case 0x0ee3:
5304       element = EL_EM_EXIT_CLOSED;
5305       break;
5306
5307     case 0x0eeb:
5308       element = EL_EM_STEEL_EXIT_OPEN;
5309       break;
5310
5311     case 0x0efc:
5312       element = EL_EM_STEEL_EXIT_CLOSED;
5313       break;
5314
5315     case 0x0f4f:        // dynamite (lit 1)
5316       element = EL_EM_DYNAMITE_ACTIVE;
5317       break;
5318
5319     case 0x0f57:        // dynamite (lit 2)
5320       element = EL_EM_DYNAMITE_ACTIVE;
5321       break;
5322
5323     case 0x0f5f:        // dynamite (lit 3)
5324       element = EL_EM_DYNAMITE_ACTIVE;
5325       break;
5326
5327     case 0x0f67:        // dynamite (lit 4)
5328       element = EL_EM_DYNAMITE_ACTIVE;
5329       break;
5330
5331     case 0x0f81:
5332     case 0x0f82:
5333     case 0x0f83:
5334     case 0x0f84:
5335       element = EL_AMOEBA_WET;
5336       break;
5337
5338     case 0x0f85:
5339       element = EL_AMOEBA_DROP;
5340       break;
5341
5342     case 0x0fb9:
5343       element = EL_DC_MAGIC_WALL;
5344       break;
5345
5346     case 0x0fd0:
5347       element = EL_SPACESHIP_UP;
5348       break;
5349
5350     case 0x0fd9:
5351       element = EL_SPACESHIP_DOWN;
5352       break;
5353
5354     case 0x0ff1:
5355       element = EL_SPACESHIP_LEFT;
5356       break;
5357
5358     case 0x0ff9:
5359       element = EL_SPACESHIP_RIGHT;
5360       break;
5361
5362     case 0x1057:
5363       element = EL_BUG_UP;
5364       break;
5365
5366     case 0x1060:
5367       element = EL_BUG_DOWN;
5368       break;
5369
5370     case 0x1078:
5371       element = EL_BUG_LEFT;
5372       break;
5373
5374     case 0x1080:
5375       element = EL_BUG_RIGHT;
5376       break;
5377
5378     case 0x10de:
5379       element = EL_MOLE_UP;
5380       break;
5381
5382     case 0x10e7:
5383       element = EL_MOLE_DOWN;
5384       break;
5385
5386     case 0x10ff:
5387       element = EL_MOLE_LEFT;
5388       break;
5389
5390     case 0x1107:
5391       element = EL_MOLE_RIGHT;
5392       break;
5393
5394     case 0x11c0:
5395       element = EL_ROBOT;
5396       break;
5397
5398     case 0x13f5:
5399       element = EL_YAMYAM_UP;
5400       break;
5401
5402     case 0x1425:
5403       element = EL_SWITCHGATE_OPEN;
5404       break;
5405
5406     case 0x1426:
5407       element = EL_SWITCHGATE_CLOSED;
5408       break;
5409
5410     case 0x1437:
5411       element = EL_DC_SWITCHGATE_SWITCH_UP;
5412       break;
5413
5414     case 0x143a:
5415       element = EL_TIMEGATE_CLOSED;
5416       break;
5417
5418     case 0x144c:        // conveyor belt switch (green)
5419       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5420       break;
5421
5422     case 0x144f:        // conveyor belt switch (red)
5423       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5424       break;
5425
5426     case 0x1452:        // conveyor belt switch (blue)
5427       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5428       break;
5429
5430     case 0x145b:
5431       element = EL_CONVEYOR_BELT_3_MIDDLE;
5432       break;
5433
5434     case 0x1463:
5435       element = EL_CONVEYOR_BELT_3_LEFT;
5436       break;
5437
5438     case 0x146b:
5439       element = EL_CONVEYOR_BELT_3_RIGHT;
5440       break;
5441
5442     case 0x1473:
5443       element = EL_CONVEYOR_BELT_1_MIDDLE;
5444       break;
5445
5446     case 0x147b:
5447       element = EL_CONVEYOR_BELT_1_LEFT;
5448       break;
5449
5450     case 0x1483:
5451       element = EL_CONVEYOR_BELT_1_RIGHT;
5452       break;
5453
5454     case 0x148b:
5455       element = EL_CONVEYOR_BELT_4_MIDDLE;
5456       break;
5457
5458     case 0x1493:
5459       element = EL_CONVEYOR_BELT_4_LEFT;
5460       break;
5461
5462     case 0x149b:
5463       element = EL_CONVEYOR_BELT_4_RIGHT;
5464       break;
5465
5466     case 0x14ac:
5467       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5468       break;
5469
5470     case 0x14bd:
5471       element = EL_EXPANDABLE_WALL_VERTICAL;
5472       break;
5473
5474     case 0x14c6:
5475       element = EL_EXPANDABLE_WALL_ANY;
5476       break;
5477
5478     case 0x14ce:        // growing steel wall (left/right)
5479       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5480       break;
5481
5482     case 0x14df:        // growing steel wall (up/down)
5483       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5484       break;
5485
5486     case 0x14e8:        // growing steel wall (up/down/left/right)
5487       element = EL_EXPANDABLE_STEELWALL_ANY;
5488       break;
5489
5490     case 0x14e9:
5491       element = EL_SHIELD_DEADLY;
5492       break;
5493
5494     case 0x1501:
5495       element = EL_EXTRA_TIME;
5496       break;
5497
5498     case 0x154f:
5499       element = EL_ACID;
5500       break;
5501
5502     case 0x1577:
5503       element = EL_EMPTY_SPACE;
5504       break;
5505
5506     case 0x1578:        // quicksand (empty)
5507       element = EL_QUICKSAND_FAST_EMPTY;
5508       break;
5509
5510     case 0x1579:        // slow quicksand (empty)
5511       element = EL_QUICKSAND_EMPTY;
5512       break;
5513
5514       // 0x157c - 0x158b:
5515       // EL_SAND
5516
5517       // 0x1590 - 0x159f:
5518       // EL_DC_LANDMINE
5519
5520     case 0x15a0:
5521       element = EL_EM_DYNAMITE;
5522       break;
5523
5524     case 0x15a1:        // key (red)
5525       element = EL_EM_KEY_1;
5526       break;
5527
5528     case 0x15a2:        // key (yellow)
5529       element = EL_EM_KEY_2;
5530       break;
5531
5532     case 0x15a3:        // key (blue)
5533       element = EL_EM_KEY_4;
5534       break;
5535
5536     case 0x15a4:        // key (green)
5537       element = EL_EM_KEY_3;
5538       break;
5539
5540     case 0x15a5:        // key (white)
5541       element = EL_DC_KEY_WHITE;
5542       break;
5543
5544     case 0x15a6:
5545       element = EL_WALL_SLIPPERY;
5546       break;
5547
5548     case 0x15a7:
5549       element = EL_WALL;
5550       break;
5551
5552     case 0x15a8:        // wall (not round)
5553       element = EL_WALL;
5554       break;
5555
5556     case 0x15a9:        // (blue)
5557       element = EL_CHAR_A;
5558       break;
5559
5560     case 0x15aa:        // (blue)
5561       element = EL_CHAR_B;
5562       break;
5563
5564     case 0x15ab:        // (blue)
5565       element = EL_CHAR_C;
5566       break;
5567
5568     case 0x15ac:        // (blue)
5569       element = EL_CHAR_D;
5570       break;
5571
5572     case 0x15ad:        // (blue)
5573       element = EL_CHAR_E;
5574       break;
5575
5576     case 0x15ae:        // (blue)
5577       element = EL_CHAR_F;
5578       break;
5579
5580     case 0x15af:        // (blue)
5581       element = EL_CHAR_G;
5582       break;
5583
5584     case 0x15b0:        // (blue)
5585       element = EL_CHAR_H;
5586       break;
5587
5588     case 0x15b1:        // (blue)
5589       element = EL_CHAR_I;
5590       break;
5591
5592     case 0x15b2:        // (blue)
5593       element = EL_CHAR_J;
5594       break;
5595
5596     case 0x15b3:        // (blue)
5597       element = EL_CHAR_K;
5598       break;
5599
5600     case 0x15b4:        // (blue)
5601       element = EL_CHAR_L;
5602       break;
5603
5604     case 0x15b5:        // (blue)
5605       element = EL_CHAR_M;
5606       break;
5607
5608     case 0x15b6:        // (blue)
5609       element = EL_CHAR_N;
5610       break;
5611
5612     case 0x15b7:        // (blue)
5613       element = EL_CHAR_O;
5614       break;
5615
5616     case 0x15b8:        // (blue)
5617       element = EL_CHAR_P;
5618       break;
5619
5620     case 0x15b9:        // (blue)
5621       element = EL_CHAR_Q;
5622       break;
5623
5624     case 0x15ba:        // (blue)
5625       element = EL_CHAR_R;
5626       break;
5627
5628     case 0x15bb:        // (blue)
5629       element = EL_CHAR_S;
5630       break;
5631
5632     case 0x15bc:        // (blue)
5633       element = EL_CHAR_T;
5634       break;
5635
5636     case 0x15bd:        // (blue)
5637       element = EL_CHAR_U;
5638       break;
5639
5640     case 0x15be:        // (blue)
5641       element = EL_CHAR_V;
5642       break;
5643
5644     case 0x15bf:        // (blue)
5645       element = EL_CHAR_W;
5646       break;
5647
5648     case 0x15c0:        // (blue)
5649       element = EL_CHAR_X;
5650       break;
5651
5652     case 0x15c1:        // (blue)
5653       element = EL_CHAR_Y;
5654       break;
5655
5656     case 0x15c2:        // (blue)
5657       element = EL_CHAR_Z;
5658       break;
5659
5660     case 0x15c3:        // (blue)
5661       element = EL_CHAR_AUMLAUT;
5662       break;
5663
5664     case 0x15c4:        // (blue)
5665       element = EL_CHAR_OUMLAUT;
5666       break;
5667
5668     case 0x15c5:        // (blue)
5669       element = EL_CHAR_UUMLAUT;
5670       break;
5671
5672     case 0x15c6:        // (blue)
5673       element = EL_CHAR_0;
5674       break;
5675
5676     case 0x15c7:        // (blue)
5677       element = EL_CHAR_1;
5678       break;
5679
5680     case 0x15c8:        // (blue)
5681       element = EL_CHAR_2;
5682       break;
5683
5684     case 0x15c9:        // (blue)
5685       element = EL_CHAR_3;
5686       break;
5687
5688     case 0x15ca:        // (blue)
5689       element = EL_CHAR_4;
5690       break;
5691
5692     case 0x15cb:        // (blue)
5693       element = EL_CHAR_5;
5694       break;
5695
5696     case 0x15cc:        // (blue)
5697       element = EL_CHAR_6;
5698       break;
5699
5700     case 0x15cd:        // (blue)
5701       element = EL_CHAR_7;
5702       break;
5703
5704     case 0x15ce:        // (blue)
5705       element = EL_CHAR_8;
5706       break;
5707
5708     case 0x15cf:        // (blue)
5709       element = EL_CHAR_9;
5710       break;
5711
5712     case 0x15d0:        // (blue)
5713       element = EL_CHAR_PERIOD;
5714       break;
5715
5716     case 0x15d1:        // (blue)
5717       element = EL_CHAR_EXCLAM;
5718       break;
5719
5720     case 0x15d2:        // (blue)
5721       element = EL_CHAR_COLON;
5722       break;
5723
5724     case 0x15d3:        // (blue)
5725       element = EL_CHAR_LESS;
5726       break;
5727
5728     case 0x15d4:        // (blue)
5729       element = EL_CHAR_GREATER;
5730       break;
5731
5732     case 0x15d5:        // (blue)
5733       element = EL_CHAR_QUESTION;
5734       break;
5735
5736     case 0x15d6:        // (blue)
5737       element = EL_CHAR_COPYRIGHT;
5738       break;
5739
5740     case 0x15d7:        // (blue)
5741       element = EL_CHAR_UP;
5742       break;
5743
5744     case 0x15d8:        // (blue)
5745       element = EL_CHAR_DOWN;
5746       break;
5747
5748     case 0x15d9:        // (blue)
5749       element = EL_CHAR_BUTTON;
5750       break;
5751
5752     case 0x15da:        // (blue)
5753       element = EL_CHAR_PLUS;
5754       break;
5755
5756     case 0x15db:        // (blue)
5757       element = EL_CHAR_MINUS;
5758       break;
5759
5760     case 0x15dc:        // (blue)
5761       element = EL_CHAR_APOSTROPHE;
5762       break;
5763
5764     case 0x15dd:        // (blue)
5765       element = EL_CHAR_PARENLEFT;
5766       break;
5767
5768     case 0x15de:        // (blue)
5769       element = EL_CHAR_PARENRIGHT;
5770       break;
5771
5772     case 0x15df:        // (green)
5773       element = EL_CHAR_A;
5774       break;
5775
5776     case 0x15e0:        // (green)
5777       element = EL_CHAR_B;
5778       break;
5779
5780     case 0x15e1:        // (green)
5781       element = EL_CHAR_C;
5782       break;
5783
5784     case 0x15e2:        // (green)
5785       element = EL_CHAR_D;
5786       break;
5787
5788     case 0x15e3:        // (green)
5789       element = EL_CHAR_E;
5790       break;
5791
5792     case 0x15e4:        // (green)
5793       element = EL_CHAR_F;
5794       break;
5795
5796     case 0x15e5:        // (green)
5797       element = EL_CHAR_G;
5798       break;
5799
5800     case 0x15e6:        // (green)
5801       element = EL_CHAR_H;
5802       break;
5803
5804     case 0x15e7:        // (green)
5805       element = EL_CHAR_I;
5806       break;
5807
5808     case 0x15e8:        // (green)
5809       element = EL_CHAR_J;
5810       break;
5811
5812     case 0x15e9:        // (green)
5813       element = EL_CHAR_K;
5814       break;
5815
5816     case 0x15ea:        // (green)
5817       element = EL_CHAR_L;
5818       break;
5819
5820     case 0x15eb:        // (green)
5821       element = EL_CHAR_M;
5822       break;
5823
5824     case 0x15ec:        // (green)
5825       element = EL_CHAR_N;
5826       break;
5827
5828     case 0x15ed:        // (green)
5829       element = EL_CHAR_O;
5830       break;
5831
5832     case 0x15ee:        // (green)
5833       element = EL_CHAR_P;
5834       break;
5835
5836     case 0x15ef:        // (green)
5837       element = EL_CHAR_Q;
5838       break;
5839
5840     case 0x15f0:        // (green)
5841       element = EL_CHAR_R;
5842       break;
5843
5844     case 0x15f1:        // (green)
5845       element = EL_CHAR_S;
5846       break;
5847
5848     case 0x15f2:        // (green)
5849       element = EL_CHAR_T;
5850       break;
5851
5852     case 0x15f3:        // (green)
5853       element = EL_CHAR_U;
5854       break;
5855
5856     case 0x15f4:        // (green)
5857       element = EL_CHAR_V;
5858       break;
5859
5860     case 0x15f5:        // (green)
5861       element = EL_CHAR_W;
5862       break;
5863
5864     case 0x15f6:        // (green)
5865       element = EL_CHAR_X;
5866       break;
5867
5868     case 0x15f7:        // (green)
5869       element = EL_CHAR_Y;
5870       break;
5871
5872     case 0x15f8:        // (green)
5873       element = EL_CHAR_Z;
5874       break;
5875
5876     case 0x15f9:        // (green)
5877       element = EL_CHAR_AUMLAUT;
5878       break;
5879
5880     case 0x15fa:        // (green)
5881       element = EL_CHAR_OUMLAUT;
5882       break;
5883
5884     case 0x15fb:        // (green)
5885       element = EL_CHAR_UUMLAUT;
5886       break;
5887
5888     case 0x15fc:        // (green)
5889       element = EL_CHAR_0;
5890       break;
5891
5892     case 0x15fd:        // (green)
5893       element = EL_CHAR_1;
5894       break;
5895
5896     case 0x15fe:        // (green)
5897       element = EL_CHAR_2;
5898       break;
5899
5900     case 0x15ff:        // (green)
5901       element = EL_CHAR_3;
5902       break;
5903
5904     case 0x1600:        // (green)
5905       element = EL_CHAR_4;
5906       break;
5907
5908     case 0x1601:        // (green)
5909       element = EL_CHAR_5;
5910       break;
5911
5912     case 0x1602:        // (green)
5913       element = EL_CHAR_6;
5914       break;
5915
5916     case 0x1603:        // (green)
5917       element = EL_CHAR_7;
5918       break;
5919
5920     case 0x1604:        // (green)
5921       element = EL_CHAR_8;
5922       break;
5923
5924     case 0x1605:        // (green)
5925       element = EL_CHAR_9;
5926       break;
5927
5928     case 0x1606:        // (green)
5929       element = EL_CHAR_PERIOD;
5930       break;
5931
5932     case 0x1607:        // (green)
5933       element = EL_CHAR_EXCLAM;
5934       break;
5935
5936     case 0x1608:        // (green)
5937       element = EL_CHAR_COLON;
5938       break;
5939
5940     case 0x1609:        // (green)
5941       element = EL_CHAR_LESS;
5942       break;
5943
5944     case 0x160a:        // (green)
5945       element = EL_CHAR_GREATER;
5946       break;
5947
5948     case 0x160b:        // (green)
5949       element = EL_CHAR_QUESTION;
5950       break;
5951
5952     case 0x160c:        // (green)
5953       element = EL_CHAR_COPYRIGHT;
5954       break;
5955
5956     case 0x160d:        // (green)
5957       element = EL_CHAR_UP;
5958       break;
5959
5960     case 0x160e:        // (green)
5961       element = EL_CHAR_DOWN;
5962       break;
5963
5964     case 0x160f:        // (green)
5965       element = EL_CHAR_BUTTON;
5966       break;
5967
5968     case 0x1610:        // (green)
5969       element = EL_CHAR_PLUS;
5970       break;
5971
5972     case 0x1611:        // (green)
5973       element = EL_CHAR_MINUS;
5974       break;
5975
5976     case 0x1612:        // (green)
5977       element = EL_CHAR_APOSTROPHE;
5978       break;
5979
5980     case 0x1613:        // (green)
5981       element = EL_CHAR_PARENLEFT;
5982       break;
5983
5984     case 0x1614:        // (green)
5985       element = EL_CHAR_PARENRIGHT;
5986       break;
5987
5988     case 0x1615:        // (blue steel)
5989       element = EL_STEEL_CHAR_A;
5990       break;
5991
5992     case 0x1616:        // (blue steel)
5993       element = EL_STEEL_CHAR_B;
5994       break;
5995
5996     case 0x1617:        // (blue steel)
5997       element = EL_STEEL_CHAR_C;
5998       break;
5999
6000     case 0x1618:        // (blue steel)
6001       element = EL_STEEL_CHAR_D;
6002       break;
6003
6004     case 0x1619:        // (blue steel)
6005       element = EL_STEEL_CHAR_E;
6006       break;
6007
6008     case 0x161a:        // (blue steel)
6009       element = EL_STEEL_CHAR_F;
6010       break;
6011
6012     case 0x161b:        // (blue steel)
6013       element = EL_STEEL_CHAR_G;
6014       break;
6015
6016     case 0x161c:        // (blue steel)
6017       element = EL_STEEL_CHAR_H;
6018       break;
6019
6020     case 0x161d:        // (blue steel)
6021       element = EL_STEEL_CHAR_I;
6022       break;
6023
6024     case 0x161e:        // (blue steel)
6025       element = EL_STEEL_CHAR_J;
6026       break;
6027
6028     case 0x161f:        // (blue steel)
6029       element = EL_STEEL_CHAR_K;
6030       break;
6031
6032     case 0x1620:        // (blue steel)
6033       element = EL_STEEL_CHAR_L;
6034       break;
6035
6036     case 0x1621:        // (blue steel)
6037       element = EL_STEEL_CHAR_M;
6038       break;
6039
6040     case 0x1622:        // (blue steel)
6041       element = EL_STEEL_CHAR_N;
6042       break;
6043
6044     case 0x1623:        // (blue steel)
6045       element = EL_STEEL_CHAR_O;
6046       break;
6047
6048     case 0x1624:        // (blue steel)
6049       element = EL_STEEL_CHAR_P;
6050       break;
6051
6052     case 0x1625:        // (blue steel)
6053       element = EL_STEEL_CHAR_Q;
6054       break;
6055
6056     case 0x1626:        // (blue steel)
6057       element = EL_STEEL_CHAR_R;
6058       break;
6059
6060     case 0x1627:        // (blue steel)
6061       element = EL_STEEL_CHAR_S;
6062       break;
6063
6064     case 0x1628:        // (blue steel)
6065       element = EL_STEEL_CHAR_T;
6066       break;
6067
6068     case 0x1629:        // (blue steel)
6069       element = EL_STEEL_CHAR_U;
6070       break;
6071
6072     case 0x162a:        // (blue steel)
6073       element = EL_STEEL_CHAR_V;
6074       break;
6075
6076     case 0x162b:        // (blue steel)
6077       element = EL_STEEL_CHAR_W;
6078       break;
6079
6080     case 0x162c:        // (blue steel)
6081       element = EL_STEEL_CHAR_X;
6082       break;
6083
6084     case 0x162d:        // (blue steel)
6085       element = EL_STEEL_CHAR_Y;
6086       break;
6087
6088     case 0x162e:        // (blue steel)
6089       element = EL_STEEL_CHAR_Z;
6090       break;
6091
6092     case 0x162f:        // (blue steel)
6093       element = EL_STEEL_CHAR_AUMLAUT;
6094       break;
6095
6096     case 0x1630:        // (blue steel)
6097       element = EL_STEEL_CHAR_OUMLAUT;
6098       break;
6099
6100     case 0x1631:        // (blue steel)
6101       element = EL_STEEL_CHAR_UUMLAUT;
6102       break;
6103
6104     case 0x1632:        // (blue steel)
6105       element = EL_STEEL_CHAR_0;
6106       break;
6107
6108     case 0x1633:        // (blue steel)
6109       element = EL_STEEL_CHAR_1;
6110       break;
6111
6112     case 0x1634:        // (blue steel)
6113       element = EL_STEEL_CHAR_2;
6114       break;
6115
6116     case 0x1635:        // (blue steel)
6117       element = EL_STEEL_CHAR_3;
6118       break;
6119
6120     case 0x1636:        // (blue steel)
6121       element = EL_STEEL_CHAR_4;
6122       break;
6123
6124     case 0x1637:        // (blue steel)
6125       element = EL_STEEL_CHAR_5;
6126       break;
6127
6128     case 0x1638:        // (blue steel)
6129       element = EL_STEEL_CHAR_6;
6130       break;
6131
6132     case 0x1639:        // (blue steel)
6133       element = EL_STEEL_CHAR_7;
6134       break;
6135
6136     case 0x163a:        // (blue steel)
6137       element = EL_STEEL_CHAR_8;
6138       break;
6139
6140     case 0x163b:        // (blue steel)
6141       element = EL_STEEL_CHAR_9;
6142       break;
6143
6144     case 0x163c:        // (blue steel)
6145       element = EL_STEEL_CHAR_PERIOD;
6146       break;
6147
6148     case 0x163d:        // (blue steel)
6149       element = EL_STEEL_CHAR_EXCLAM;
6150       break;
6151
6152     case 0x163e:        // (blue steel)
6153       element = EL_STEEL_CHAR_COLON;
6154       break;
6155
6156     case 0x163f:        // (blue steel)
6157       element = EL_STEEL_CHAR_LESS;
6158       break;
6159
6160     case 0x1640:        // (blue steel)
6161       element = EL_STEEL_CHAR_GREATER;
6162       break;
6163
6164     case 0x1641:        // (blue steel)
6165       element = EL_STEEL_CHAR_QUESTION;
6166       break;
6167
6168     case 0x1642:        // (blue steel)
6169       element = EL_STEEL_CHAR_COPYRIGHT;
6170       break;
6171
6172     case 0x1643:        // (blue steel)
6173       element = EL_STEEL_CHAR_UP;
6174       break;
6175
6176     case 0x1644:        // (blue steel)
6177       element = EL_STEEL_CHAR_DOWN;
6178       break;
6179
6180     case 0x1645:        // (blue steel)
6181       element = EL_STEEL_CHAR_BUTTON;
6182       break;
6183
6184     case 0x1646:        // (blue steel)
6185       element = EL_STEEL_CHAR_PLUS;
6186       break;
6187
6188     case 0x1647:        // (blue steel)
6189       element = EL_STEEL_CHAR_MINUS;
6190       break;
6191
6192     case 0x1648:        // (blue steel)
6193       element = EL_STEEL_CHAR_APOSTROPHE;
6194       break;
6195
6196     case 0x1649:        // (blue steel)
6197       element = EL_STEEL_CHAR_PARENLEFT;
6198       break;
6199
6200     case 0x164a:        // (blue steel)
6201       element = EL_STEEL_CHAR_PARENRIGHT;
6202       break;
6203
6204     case 0x164b:        // (green steel)
6205       element = EL_STEEL_CHAR_A;
6206       break;
6207
6208     case 0x164c:        // (green steel)
6209       element = EL_STEEL_CHAR_B;
6210       break;
6211
6212     case 0x164d:        // (green steel)
6213       element = EL_STEEL_CHAR_C;
6214       break;
6215
6216     case 0x164e:        // (green steel)
6217       element = EL_STEEL_CHAR_D;
6218       break;
6219
6220     case 0x164f:        // (green steel)
6221       element = EL_STEEL_CHAR_E;
6222       break;
6223
6224     case 0x1650:        // (green steel)
6225       element = EL_STEEL_CHAR_F;
6226       break;
6227
6228     case 0x1651:        // (green steel)
6229       element = EL_STEEL_CHAR_G;
6230       break;
6231
6232     case 0x1652:        // (green steel)
6233       element = EL_STEEL_CHAR_H;
6234       break;
6235
6236     case 0x1653:        // (green steel)
6237       element = EL_STEEL_CHAR_I;
6238       break;
6239
6240     case 0x1654:        // (green steel)
6241       element = EL_STEEL_CHAR_J;
6242       break;
6243
6244     case 0x1655:        // (green steel)
6245       element = EL_STEEL_CHAR_K;
6246       break;
6247
6248     case 0x1656:        // (green steel)
6249       element = EL_STEEL_CHAR_L;
6250       break;
6251
6252     case 0x1657:        // (green steel)
6253       element = EL_STEEL_CHAR_M;
6254       break;
6255
6256     case 0x1658:        // (green steel)
6257       element = EL_STEEL_CHAR_N;
6258       break;
6259
6260     case 0x1659:        // (green steel)
6261       element = EL_STEEL_CHAR_O;
6262       break;
6263
6264     case 0x165a:        // (green steel)
6265       element = EL_STEEL_CHAR_P;
6266       break;
6267
6268     case 0x165b:        // (green steel)
6269       element = EL_STEEL_CHAR_Q;
6270       break;
6271
6272     case 0x165c:        // (green steel)
6273       element = EL_STEEL_CHAR_R;
6274       break;
6275
6276     case 0x165d:        // (green steel)
6277       element = EL_STEEL_CHAR_S;
6278       break;
6279
6280     case 0x165e:        // (green steel)
6281       element = EL_STEEL_CHAR_T;
6282       break;
6283
6284     case 0x165f:        // (green steel)
6285       element = EL_STEEL_CHAR_U;
6286       break;
6287
6288     case 0x1660:        // (green steel)
6289       element = EL_STEEL_CHAR_V;
6290       break;
6291
6292     case 0x1661:        // (green steel)
6293       element = EL_STEEL_CHAR_W;
6294       break;
6295
6296     case 0x1662:        // (green steel)
6297       element = EL_STEEL_CHAR_X;
6298       break;
6299
6300     case 0x1663:        // (green steel)
6301       element = EL_STEEL_CHAR_Y;
6302       break;
6303
6304     case 0x1664:        // (green steel)
6305       element = EL_STEEL_CHAR_Z;
6306       break;
6307
6308     case 0x1665:        // (green steel)
6309       element = EL_STEEL_CHAR_AUMLAUT;
6310       break;
6311
6312     case 0x1666:        // (green steel)
6313       element = EL_STEEL_CHAR_OUMLAUT;
6314       break;
6315
6316     case 0x1667:        // (green steel)
6317       element = EL_STEEL_CHAR_UUMLAUT;
6318       break;
6319
6320     case 0x1668:        // (green steel)
6321       element = EL_STEEL_CHAR_0;
6322       break;
6323
6324     case 0x1669:        // (green steel)
6325       element = EL_STEEL_CHAR_1;
6326       break;
6327
6328     case 0x166a:        // (green steel)
6329       element = EL_STEEL_CHAR_2;
6330       break;
6331
6332     case 0x166b:        // (green steel)
6333       element = EL_STEEL_CHAR_3;
6334       break;
6335
6336     case 0x166c:        // (green steel)
6337       element = EL_STEEL_CHAR_4;
6338       break;
6339
6340     case 0x166d:        // (green steel)
6341       element = EL_STEEL_CHAR_5;
6342       break;
6343
6344     case 0x166e:        // (green steel)
6345       element = EL_STEEL_CHAR_6;
6346       break;
6347
6348     case 0x166f:        // (green steel)
6349       element = EL_STEEL_CHAR_7;
6350       break;
6351
6352     case 0x1670:        // (green steel)
6353       element = EL_STEEL_CHAR_8;
6354       break;
6355
6356     case 0x1671:        // (green steel)
6357       element = EL_STEEL_CHAR_9;
6358       break;
6359
6360     case 0x1672:        // (green steel)
6361       element = EL_STEEL_CHAR_PERIOD;
6362       break;
6363
6364     case 0x1673:        // (green steel)
6365       element = EL_STEEL_CHAR_EXCLAM;
6366       break;
6367
6368     case 0x1674:        // (green steel)
6369       element = EL_STEEL_CHAR_COLON;
6370       break;
6371
6372     case 0x1675:        // (green steel)
6373       element = EL_STEEL_CHAR_LESS;
6374       break;
6375
6376     case 0x1676:        // (green steel)
6377       element = EL_STEEL_CHAR_GREATER;
6378       break;
6379
6380     case 0x1677:        // (green steel)
6381       element = EL_STEEL_CHAR_QUESTION;
6382       break;
6383
6384     case 0x1678:        // (green steel)
6385       element = EL_STEEL_CHAR_COPYRIGHT;
6386       break;
6387
6388     case 0x1679:        // (green steel)
6389       element = EL_STEEL_CHAR_UP;
6390       break;
6391
6392     case 0x167a:        // (green steel)
6393       element = EL_STEEL_CHAR_DOWN;
6394       break;
6395
6396     case 0x167b:        // (green steel)
6397       element = EL_STEEL_CHAR_BUTTON;
6398       break;
6399
6400     case 0x167c:        // (green steel)
6401       element = EL_STEEL_CHAR_PLUS;
6402       break;
6403
6404     case 0x167d:        // (green steel)
6405       element = EL_STEEL_CHAR_MINUS;
6406       break;
6407
6408     case 0x167e:        // (green steel)
6409       element = EL_STEEL_CHAR_APOSTROPHE;
6410       break;
6411
6412     case 0x167f:        // (green steel)
6413       element = EL_STEEL_CHAR_PARENLEFT;
6414       break;
6415
6416     case 0x1680:        // (green steel)
6417       element = EL_STEEL_CHAR_PARENRIGHT;
6418       break;
6419
6420     case 0x1681:        // gate (red)
6421       element = EL_EM_GATE_1;
6422       break;
6423
6424     case 0x1682:        // secret gate (red)
6425       element = EL_EM_GATE_1_GRAY;
6426       break;
6427
6428     case 0x1683:        // gate (yellow)
6429       element = EL_EM_GATE_2;
6430       break;
6431
6432     case 0x1684:        // secret gate (yellow)
6433       element = EL_EM_GATE_2_GRAY;
6434       break;
6435
6436     case 0x1685:        // gate (blue)
6437       element = EL_EM_GATE_4;
6438       break;
6439
6440     case 0x1686:        // secret gate (blue)
6441       element = EL_EM_GATE_4_GRAY;
6442       break;
6443
6444     case 0x1687:        // gate (green)
6445       element = EL_EM_GATE_3;
6446       break;
6447
6448     case 0x1688:        // secret gate (green)
6449       element = EL_EM_GATE_3_GRAY;
6450       break;
6451
6452     case 0x1689:        // gate (white)
6453       element = EL_DC_GATE_WHITE;
6454       break;
6455
6456     case 0x168a:        // secret gate (white)
6457       element = EL_DC_GATE_WHITE_GRAY;
6458       break;
6459
6460     case 0x168b:        // secret gate (no key)
6461       element = EL_DC_GATE_FAKE_GRAY;
6462       break;
6463
6464     case 0x168c:
6465       element = EL_ROBOT_WHEEL;
6466       break;
6467
6468     case 0x168d:
6469       element = EL_DC_TIMEGATE_SWITCH;
6470       break;
6471
6472     case 0x168e:
6473       element = EL_ACID_POOL_BOTTOM;
6474       break;
6475
6476     case 0x168f:
6477       element = EL_ACID_POOL_TOPLEFT;
6478       break;
6479
6480     case 0x1690:
6481       element = EL_ACID_POOL_TOPRIGHT;
6482       break;
6483
6484     case 0x1691:
6485       element = EL_ACID_POOL_BOTTOMLEFT;
6486       break;
6487
6488     case 0x1692:
6489       element = EL_ACID_POOL_BOTTOMRIGHT;
6490       break;
6491
6492     case 0x1693:
6493       element = EL_STEELWALL;
6494       break;
6495
6496     case 0x1694:
6497       element = EL_STEELWALL_SLIPPERY;
6498       break;
6499
6500     case 0x1695:        // steel wall (not round)
6501       element = EL_STEELWALL;
6502       break;
6503
6504     case 0x1696:        // steel wall (left)
6505       element = EL_DC_STEELWALL_1_LEFT;
6506       break;
6507
6508     case 0x1697:        // steel wall (bottom)
6509       element = EL_DC_STEELWALL_1_BOTTOM;
6510       break;
6511
6512     case 0x1698:        // steel wall (right)
6513       element = EL_DC_STEELWALL_1_RIGHT;
6514       break;
6515
6516     case 0x1699:        // steel wall (top)
6517       element = EL_DC_STEELWALL_1_TOP;
6518       break;
6519
6520     case 0x169a:        // steel wall (left/bottom)
6521       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6522       break;
6523
6524     case 0x169b:        // steel wall (right/bottom)
6525       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6526       break;
6527
6528     case 0x169c:        // steel wall (right/top)
6529       element = EL_DC_STEELWALL_1_TOPRIGHT;
6530       break;
6531
6532     case 0x169d:        // steel wall (left/top)
6533       element = EL_DC_STEELWALL_1_TOPLEFT;
6534       break;
6535
6536     case 0x169e:        // steel wall (right/bottom small)
6537       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6538       break;
6539
6540     case 0x169f:        // steel wall (left/bottom small)
6541       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6542       break;
6543
6544     case 0x16a0:        // steel wall (right/top small)
6545       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6546       break;
6547
6548     case 0x16a1:        // steel wall (left/top small)
6549       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6550       break;
6551
6552     case 0x16a2:        // steel wall (left/right)
6553       element = EL_DC_STEELWALL_1_VERTICAL;
6554       break;
6555
6556     case 0x16a3:        // steel wall (top/bottom)
6557       element = EL_DC_STEELWALL_1_HORIZONTAL;
6558       break;
6559
6560     case 0x16a4:        // steel wall 2 (left end)
6561       element = EL_DC_STEELWALL_2_LEFT;
6562       break;
6563
6564     case 0x16a5:        // steel wall 2 (right end)
6565       element = EL_DC_STEELWALL_2_RIGHT;
6566       break;
6567
6568     case 0x16a6:        // steel wall 2 (top end)
6569       element = EL_DC_STEELWALL_2_TOP;
6570       break;
6571
6572     case 0x16a7:        // steel wall 2 (bottom end)
6573       element = EL_DC_STEELWALL_2_BOTTOM;
6574       break;
6575
6576     case 0x16a8:        // steel wall 2 (left/right)
6577       element = EL_DC_STEELWALL_2_HORIZONTAL;
6578       break;
6579
6580     case 0x16a9:        // steel wall 2 (up/down)
6581       element = EL_DC_STEELWALL_2_VERTICAL;
6582       break;
6583
6584     case 0x16aa:        // steel wall 2 (mid)
6585       element = EL_DC_STEELWALL_2_MIDDLE;
6586       break;
6587
6588     case 0x16ab:
6589       element = EL_SIGN_EXCLAMATION;
6590       break;
6591
6592     case 0x16ac:
6593       element = EL_SIGN_RADIOACTIVITY;
6594       break;
6595
6596     case 0x16ad:
6597       element = EL_SIGN_STOP;
6598       break;
6599
6600     case 0x16ae:
6601       element = EL_SIGN_WHEELCHAIR;
6602       break;
6603
6604     case 0x16af:
6605       element = EL_SIGN_PARKING;
6606       break;
6607
6608     case 0x16b0:
6609       element = EL_SIGN_NO_ENTRY;
6610       break;
6611
6612     case 0x16b1:
6613       element = EL_SIGN_HEART;
6614       break;
6615
6616     case 0x16b2:
6617       element = EL_SIGN_GIVE_WAY;
6618       break;
6619
6620     case 0x16b3:
6621       element = EL_SIGN_ENTRY_FORBIDDEN;
6622       break;
6623
6624     case 0x16b4:
6625       element = EL_SIGN_EMERGENCY_EXIT;
6626       break;
6627
6628     case 0x16b5:
6629       element = EL_SIGN_YIN_YANG;
6630       break;
6631
6632     case 0x16b6:
6633       element = EL_WALL_EMERALD;
6634       break;
6635
6636     case 0x16b7:
6637       element = EL_WALL_DIAMOND;
6638       break;
6639
6640     case 0x16b8:
6641       element = EL_WALL_PEARL;
6642       break;
6643
6644     case 0x16b9:
6645       element = EL_WALL_CRYSTAL;
6646       break;
6647
6648     case 0x16ba:
6649       element = EL_INVISIBLE_WALL;
6650       break;
6651
6652     case 0x16bb:
6653       element = EL_INVISIBLE_STEELWALL;
6654       break;
6655
6656       // 0x16bc - 0x16cb:
6657       // EL_INVISIBLE_SAND
6658
6659     case 0x16cc:
6660       element = EL_LIGHT_SWITCH;
6661       break;
6662
6663     case 0x16cd:
6664       element = EL_ENVELOPE_1;
6665       break;
6666
6667     default:
6668       if (element >= 0x0117 && element <= 0x036e)       // (?)
6669         element = EL_DIAMOND;
6670       else if (element >= 0x042d && element <= 0x0684)  // (?)
6671         element = EL_EMERALD;
6672       else if (element >= 0x157c && element <= 0x158b)
6673         element = EL_SAND;
6674       else if (element >= 0x1590 && element <= 0x159f)
6675         element = EL_DC_LANDMINE;
6676       else if (element >= 0x16bc && element <= 0x16cb)
6677         element = EL_INVISIBLE_SAND;
6678       else
6679       {
6680         Warn("unknown Diamond Caves element 0x%04x", element);
6681
6682         element = EL_UNKNOWN;
6683       }
6684       break;
6685   }
6686
6687   return getMappedElement(element);
6688 }
6689
6690 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6691 {
6692   byte header[DC_LEVEL_HEADER_SIZE];
6693   int envelope_size;
6694   int envelope_header_pos = 62;
6695   int envelope_content_pos = 94;
6696   int level_name_pos = 251;
6697   int level_author_pos = 292;
6698   int envelope_header_len;
6699   int envelope_content_len;
6700   int level_name_len;
6701   int level_author_len;
6702   int fieldx, fieldy;
6703   int num_yamyam_contents;
6704   int i, x, y;
6705
6706   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6707
6708   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6709   {
6710     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6711
6712     header[i * 2 + 0] = header_word >> 8;
6713     header[i * 2 + 1] = header_word & 0xff;
6714   }
6715
6716   // read some values from level header to check level decoding integrity
6717   fieldx = header[6] | (header[7] << 8);
6718   fieldy = header[8] | (header[9] << 8);
6719   num_yamyam_contents = header[60] | (header[61] << 8);
6720
6721   // do some simple sanity checks to ensure that level was correctly decoded
6722   if (fieldx < 1 || fieldx > 256 ||
6723       fieldy < 1 || fieldy > 256 ||
6724       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6725   {
6726     level->no_valid_file = TRUE;
6727
6728     Warn("cannot decode level from stream -- using empty level");
6729
6730     return;
6731   }
6732
6733   // maximum envelope header size is 31 bytes
6734   envelope_header_len   = header[envelope_header_pos];
6735   // maximum envelope content size is 110 (156?) bytes
6736   envelope_content_len  = header[envelope_content_pos];
6737
6738   // maximum level title size is 40 bytes
6739   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6740   // maximum level author size is 30 (51?) bytes
6741   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6742
6743   envelope_size = 0;
6744
6745   for (i = 0; i < envelope_header_len; i++)
6746     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6747       level->envelope[0].text[envelope_size++] =
6748         header[envelope_header_pos + 1 + i];
6749
6750   if (envelope_header_len > 0 && envelope_content_len > 0)
6751   {
6752     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6753       level->envelope[0].text[envelope_size++] = '\n';
6754     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6755       level->envelope[0].text[envelope_size++] = '\n';
6756   }
6757
6758   for (i = 0; i < envelope_content_len; i++)
6759     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6760       level->envelope[0].text[envelope_size++] =
6761         header[envelope_content_pos + 1 + i];
6762
6763   level->envelope[0].text[envelope_size] = '\0';
6764
6765   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6766   level->envelope[0].ysize = 10;
6767   level->envelope[0].autowrap = TRUE;
6768   level->envelope[0].centered = TRUE;
6769
6770   for (i = 0; i < level_name_len; i++)
6771     level->name[i] = header[level_name_pos + 1 + i];
6772   level->name[level_name_len] = '\0';
6773
6774   for (i = 0; i < level_author_len; i++)
6775     level->author[i] = header[level_author_pos + 1 + i];
6776   level->author[level_author_len] = '\0';
6777
6778   num_yamyam_contents = header[60] | (header[61] << 8);
6779   level->num_yamyam_contents =
6780     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6781
6782   for (i = 0; i < num_yamyam_contents; i++)
6783   {
6784     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6785     {
6786       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6787       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6788
6789       if (i < MAX_ELEMENT_CONTENTS)
6790         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6791     }
6792   }
6793
6794   fieldx = header[6] | (header[7] << 8);
6795   fieldy = header[8] | (header[9] << 8);
6796   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6797   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6798
6799   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6800   {
6801     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6802     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6803
6804     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6805       level->field[x][y] = getMappedElement_DC(element_dc);
6806   }
6807
6808   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6809   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6810   level->field[x][y] = EL_PLAYER_1;
6811
6812   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6813   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6814   level->field[x][y] = EL_PLAYER_2;
6815
6816   level->gems_needed            = header[18] | (header[19] << 8);
6817
6818   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6819   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6820   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6821   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6822   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6823   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6824   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6825   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6826   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6827   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6828   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6829   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6830
6831   level->time                   = header[44] | (header[45] << 8);
6832
6833   level->amoeba_speed           = header[46] | (header[47] << 8);
6834   level->time_light             = header[48] | (header[49] << 8);
6835   level->time_timegate          = header[50] | (header[51] << 8);
6836   level->time_wheel             = header[52] | (header[53] << 8);
6837   level->time_magic_wall        = header[54] | (header[55] << 8);
6838   level->extra_time             = header[56] | (header[57] << 8);
6839   level->shield_normal_time     = header[58] | (header[59] << 8);
6840
6841   // shield and extra time elements do not have a score
6842   level->score[SC_SHIELD]       = 0;
6843   level->extra_time_score       = 0;
6844
6845   // set time for normal and deadly shields to the same value
6846   level->shield_deadly_time     = level->shield_normal_time;
6847
6848   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6849   // can slip down from flat walls, like normal walls and steel walls
6850   level->em_slippery_gems = TRUE;
6851
6852   // time score is counted for each 10 seconds left in Diamond Caves levels
6853   level->time_score_base = 10;
6854 }
6855
6856 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6857                                      struct LevelFileInfo *level_file_info,
6858                                      boolean level_info_only)
6859 {
6860   char *filename = level_file_info->filename;
6861   File *file;
6862   int num_magic_bytes = 8;
6863   char magic_bytes[num_magic_bytes + 1];
6864   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6865
6866   if (!(file = openFile(filename, MODE_READ)))
6867   {
6868     level->no_valid_file = TRUE;
6869
6870     if (!level_info_only)
6871       Warn("cannot read level '%s' -- using empty level", filename);
6872
6873     return;
6874   }
6875
6876   // fseek(file, 0x0000, SEEK_SET);
6877
6878   if (level_file_info->packed)
6879   {
6880     // read "magic bytes" from start of file
6881     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6882       magic_bytes[0] = '\0';
6883
6884     // check "magic bytes" for correct file format
6885     if (!strPrefix(magic_bytes, "DC2"))
6886     {
6887       level->no_valid_file = TRUE;
6888
6889       Warn("unknown DC level file '%s' -- using empty level", filename);
6890
6891       return;
6892     }
6893
6894     if (strPrefix(magic_bytes, "DC2Win95") ||
6895         strPrefix(magic_bytes, "DC2Win98"))
6896     {
6897       int position_first_level = 0x00fa;
6898       int extra_bytes = 4;
6899       int skip_bytes;
6900
6901       // advance file stream to first level inside the level package
6902       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6903
6904       // each block of level data is followed by block of non-level data
6905       num_levels_to_skip *= 2;
6906
6907       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6908       while (num_levels_to_skip >= 0)
6909       {
6910         // advance file stream to next level inside the level package
6911         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6912         {
6913           level->no_valid_file = TRUE;
6914
6915           Warn("cannot fseek in file '%s' -- using empty level", filename);
6916
6917           return;
6918         }
6919
6920         // skip apparently unused extra bytes following each level
6921         ReadUnusedBytesFromFile(file, extra_bytes);
6922
6923         // read size of next level in level package
6924         skip_bytes = getFile32BitLE(file);
6925
6926         num_levels_to_skip--;
6927       }
6928     }
6929     else
6930     {
6931       level->no_valid_file = TRUE;
6932
6933       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6934
6935       return;
6936     }
6937   }
6938
6939   LoadLevelFromFileStream_DC(file, level);
6940
6941   closeFile(file);
6942 }
6943
6944
6945 // ----------------------------------------------------------------------------
6946 // functions for loading SB level
6947 // ----------------------------------------------------------------------------
6948
6949 int getMappedElement_SB(int element_ascii, boolean use_ces)
6950 {
6951   static struct
6952   {
6953     int ascii;
6954     int sb;
6955     int ce;
6956   }
6957   sb_element_mapping[] =
6958   {
6959     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6960     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6961     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6962     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6963     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6964     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6965     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6966     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6967
6968     { 0,   -1,                      -1          },
6969   };
6970
6971   int i;
6972
6973   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6974     if (element_ascii == sb_element_mapping[i].ascii)
6975       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6976
6977   return EL_UNDEFINED;
6978 }
6979
6980 static void SetLevelSettings_SB(struct LevelInfo *level)
6981 {
6982   // time settings
6983   level->time = 0;
6984   level->use_step_counter = TRUE;
6985
6986   // score settings
6987   level->score[SC_TIME_BONUS] = 0;
6988   level->time_score_base = 1;
6989   level->rate_time_over_score = TRUE;
6990
6991   // game settings
6992   level->auto_exit_sokoban = TRUE;
6993 }
6994
6995 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6996                                      struct LevelFileInfo *level_file_info,
6997                                      boolean level_info_only)
6998 {
6999   char *filename = level_file_info->filename;
7000   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7001   char last_comment[MAX_LINE_LEN];
7002   char level_name[MAX_LINE_LEN];
7003   char *line_ptr;
7004   File *file;
7005   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7006   boolean read_continued_line = FALSE;
7007   boolean reading_playfield = FALSE;
7008   boolean got_valid_playfield_line = FALSE;
7009   boolean invalid_playfield_char = FALSE;
7010   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7011   int file_level_nr = 0;
7012   int x = 0, y = 0;             // initialized to make compilers happy
7013
7014   last_comment[0] = '\0';
7015   level_name[0] = '\0';
7016
7017   if (!(file = openFile(filename, MODE_READ)))
7018   {
7019     level->no_valid_file = TRUE;
7020
7021     if (!level_info_only)
7022       Warn("cannot read level '%s' -- using empty level", filename);
7023
7024     return;
7025   }
7026
7027   while (!checkEndOfFile(file))
7028   {
7029     // level successfully read, but next level may follow here
7030     if (!got_valid_playfield_line && reading_playfield)
7031     {
7032       // read playfield from single level file -- skip remaining file
7033       if (!level_file_info->packed)
7034         break;
7035
7036       if (file_level_nr >= num_levels_to_skip)
7037         break;
7038
7039       file_level_nr++;
7040
7041       last_comment[0] = '\0';
7042       level_name[0] = '\0';
7043
7044       reading_playfield = FALSE;
7045     }
7046
7047     got_valid_playfield_line = FALSE;
7048
7049     // read next line of input file
7050     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7051       break;
7052
7053     // cut trailing line break (this can be newline and/or carriage return)
7054     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7055       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7056         *line_ptr = '\0';
7057
7058     // copy raw input line for later use (mainly debugging output)
7059     strcpy(line_raw, line);
7060
7061     if (read_continued_line)
7062     {
7063       // append new line to existing line, if there is enough space
7064       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7065         strcat(previous_line, line_ptr);
7066
7067       strcpy(line, previous_line);      // copy storage buffer to line
7068
7069       read_continued_line = FALSE;
7070     }
7071
7072     // if the last character is '\', continue at next line
7073     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7074     {
7075       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7076       strcpy(previous_line, line);      // copy line to storage buffer
7077
7078       read_continued_line = TRUE;
7079
7080       continue;
7081     }
7082
7083     // skip empty lines
7084     if (line[0] == '\0')
7085       continue;
7086
7087     // extract comment text from comment line
7088     if (line[0] == ';')
7089     {
7090       for (line_ptr = line; *line_ptr; line_ptr++)
7091         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7092           break;
7093
7094       strcpy(last_comment, line_ptr);
7095
7096       continue;
7097     }
7098
7099     // extract level title text from line containing level title
7100     if (line[0] == '\'')
7101     {
7102       strcpy(level_name, &line[1]);
7103
7104       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7105         level_name[strlen(level_name) - 1] = '\0';
7106
7107       continue;
7108     }
7109
7110     // skip lines containing only spaces (or empty lines)
7111     for (line_ptr = line; *line_ptr; line_ptr++)
7112       if (*line_ptr != ' ')
7113         break;
7114     if (*line_ptr == '\0')
7115       continue;
7116
7117     // at this point, we have found a line containing part of a playfield
7118
7119     got_valid_playfield_line = TRUE;
7120
7121     if (!reading_playfield)
7122     {
7123       reading_playfield = TRUE;
7124       invalid_playfield_char = FALSE;
7125
7126       for (x = 0; x < MAX_LEV_FIELDX; x++)
7127         for (y = 0; y < MAX_LEV_FIELDY; y++)
7128           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7129
7130       level->fieldx = 0;
7131       level->fieldy = 0;
7132
7133       // start with topmost tile row
7134       y = 0;
7135     }
7136
7137     // skip playfield line if larger row than allowed
7138     if (y >= MAX_LEV_FIELDY)
7139       continue;
7140
7141     // start with leftmost tile column
7142     x = 0;
7143
7144     // read playfield elements from line
7145     for (line_ptr = line; *line_ptr; line_ptr++)
7146     {
7147       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7148
7149       // stop parsing playfield line if larger column than allowed
7150       if (x >= MAX_LEV_FIELDX)
7151         break;
7152
7153       if (mapped_sb_element == EL_UNDEFINED)
7154       {
7155         invalid_playfield_char = TRUE;
7156
7157         break;
7158       }
7159
7160       level->field[x][y] = mapped_sb_element;
7161
7162       // continue with next tile column
7163       x++;
7164
7165       level->fieldx = MAX(x, level->fieldx);
7166     }
7167
7168     if (invalid_playfield_char)
7169     {
7170       // if first playfield line, treat invalid lines as comment lines
7171       if (y == 0)
7172         reading_playfield = FALSE;
7173
7174       continue;
7175     }
7176
7177     // continue with next tile row
7178     y++;
7179   }
7180
7181   closeFile(file);
7182
7183   level->fieldy = y;
7184
7185   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7186   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7187
7188   if (!reading_playfield)
7189   {
7190     level->no_valid_file = TRUE;
7191
7192     Warn("cannot read level '%s' -- using empty level", filename);
7193
7194     return;
7195   }
7196
7197   if (*level_name != '\0')
7198   {
7199     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7200     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7201   }
7202   else if (*last_comment != '\0')
7203   {
7204     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7205     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7206   }
7207   else
7208   {
7209     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7210   }
7211
7212   // set all empty fields beyond the border walls to invisible steel wall
7213   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7214   {
7215     if ((x == 0 || x == level->fieldx - 1 ||
7216          y == 0 || y == level->fieldy - 1) &&
7217         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7218       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7219                      level->field, level->fieldx, level->fieldy);
7220   }
7221
7222   // set special level settings for Sokoban levels
7223   SetLevelSettings_SB(level);
7224
7225   if (load_xsb_to_ces)
7226   {
7227     // special global settings can now be set in level template
7228     level->use_custom_template = TRUE;
7229   }
7230 }
7231
7232
7233 // -------------------------------------------------------------------------
7234 // functions for handling native levels
7235 // -------------------------------------------------------------------------
7236
7237 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7238                                      struct LevelFileInfo *level_file_info,
7239                                      boolean level_info_only)
7240 {
7241   int pos = 0;
7242
7243   // determine position of requested level inside level package
7244   if (level_file_info->packed)
7245     pos = level_file_info->nr - leveldir_current->first_level;
7246
7247   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7248     level->no_valid_file = TRUE;
7249 }
7250
7251 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7252                                      struct LevelFileInfo *level_file_info,
7253                                      boolean level_info_only)
7254 {
7255   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7256     level->no_valid_file = TRUE;
7257 }
7258
7259 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7260                                      struct LevelFileInfo *level_file_info,
7261                                      boolean level_info_only)
7262 {
7263   int pos = 0;
7264
7265   // determine position of requested level inside level package
7266   if (level_file_info->packed)
7267     pos = level_file_info->nr - leveldir_current->first_level;
7268
7269   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7270     level->no_valid_file = TRUE;
7271 }
7272
7273 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7274                                      struct LevelFileInfo *level_file_info,
7275                                      boolean level_info_only)
7276 {
7277   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7278     level->no_valid_file = TRUE;
7279 }
7280
7281 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7282 {
7283   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7284     CopyNativeLevel_RND_to_BD(level);
7285   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7286     CopyNativeLevel_RND_to_EM(level);
7287   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7288     CopyNativeLevel_RND_to_SP(level);
7289   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7290     CopyNativeLevel_RND_to_MM(level);
7291 }
7292
7293 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7294 {
7295   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7296     CopyNativeLevel_BD_to_RND(level);
7297   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7298     CopyNativeLevel_EM_to_RND(level);
7299   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7300     CopyNativeLevel_SP_to_RND(level);
7301   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7302     CopyNativeLevel_MM_to_RND(level);
7303 }
7304
7305 void SaveNativeLevel(struct LevelInfo *level)
7306 {
7307   // saving native level files only supported for some game engines
7308   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7309       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7310     return;
7311
7312   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7313                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7314   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7315   char *filename = getLevelFilenameFromBasename(basename);
7316
7317   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7318     return;
7319
7320   boolean success = FALSE;
7321
7322   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7323   {
7324     CopyNativeLevel_RND_to_BD(level);
7325     // CopyNativeTape_RND_to_BD(level);
7326
7327     success = SaveNativeLevel_BD(filename);
7328   }
7329   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7330   {
7331     CopyNativeLevel_RND_to_SP(level);
7332     CopyNativeTape_RND_to_SP(level);
7333
7334     success = SaveNativeLevel_SP(filename);
7335   }
7336
7337   if (success)
7338     Request("Native level file saved!", REQ_CONFIRM);
7339   else
7340     Request("Failed to save native level file!", REQ_CONFIRM);
7341 }
7342
7343
7344 // ----------------------------------------------------------------------------
7345 // functions for loading generic level
7346 // ----------------------------------------------------------------------------
7347
7348 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7349                                   struct LevelFileInfo *level_file_info,
7350                                   boolean level_info_only)
7351 {
7352   // always start with reliable default values
7353   setLevelInfoToDefaults(level, level_info_only, TRUE);
7354
7355   switch (level_file_info->type)
7356   {
7357     case LEVEL_FILE_TYPE_RND:
7358       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7359       break;
7360
7361     case LEVEL_FILE_TYPE_BD:
7362       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7363       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7364       break;
7365
7366     case LEVEL_FILE_TYPE_EM:
7367       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7368       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7369       break;
7370
7371     case LEVEL_FILE_TYPE_SP:
7372       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7373       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7374       break;
7375
7376     case LEVEL_FILE_TYPE_MM:
7377       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7378       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7379       break;
7380
7381     case LEVEL_FILE_TYPE_DC:
7382       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7383       break;
7384
7385     case LEVEL_FILE_TYPE_SB:
7386       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7387       break;
7388
7389     default:
7390       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7391       break;
7392   }
7393
7394   // if level file is invalid, restore level structure to default values
7395   if (level->no_valid_file)
7396     setLevelInfoToDefaults(level, level_info_only, FALSE);
7397
7398   if (check_special_flags("use_native_bd_game_engine"))
7399     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7400
7401   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7402     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7403
7404   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7405     CopyNativeLevel_Native_to_RND(level);
7406 }
7407
7408 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7409 {
7410   static struct LevelFileInfo level_file_info;
7411
7412   // always start with reliable default values
7413   setFileInfoToDefaults(&level_file_info);
7414
7415   level_file_info.nr = 0;                       // unknown level number
7416   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7417
7418   setString(&level_file_info.filename, filename);
7419
7420   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7421 }
7422
7423 static void LoadLevel_InitVersion(struct LevelInfo *level)
7424 {
7425   int i, j;
7426
7427   if (leveldir_current == NULL)         // only when dumping level
7428     return;
7429
7430   // all engine modifications also valid for levels which use latest engine
7431   if (level->game_version < VERSION_IDENT(3,2,0,5))
7432   {
7433     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7434     level->time_score_base = 10;
7435   }
7436
7437   if (leveldir_current->latest_engine)
7438   {
7439     // ---------- use latest game engine --------------------------------------
7440
7441     /* For all levels which are forced to use the latest game engine version
7442        (normally all but user contributed, private and undefined levels), set
7443        the game engine version to the actual version; this allows for actual
7444        corrections in the game engine to take effect for existing, converted
7445        levels (from "classic" or other existing games) to make the emulation
7446        of the corresponding game more accurate, while (hopefully) not breaking
7447        existing levels created from other players. */
7448
7449     level->game_version = GAME_VERSION_ACTUAL;
7450
7451     /* Set special EM style gems behaviour: EM style gems slip down from
7452        normal, steel and growing wall. As this is a more fundamental change,
7453        it seems better to set the default behaviour to "off" (as it is more
7454        natural) and make it configurable in the level editor (as a property
7455        of gem style elements). Already existing converted levels (neither
7456        private nor contributed levels) are changed to the new behaviour. */
7457
7458     if (level->file_version < FILE_VERSION_2_0)
7459       level->em_slippery_gems = TRUE;
7460
7461     return;
7462   }
7463
7464   // ---------- use game engine the level was created with --------------------
7465
7466   /* For all levels which are not forced to use the latest game engine
7467      version (normally user contributed, private and undefined levels),
7468      use the version of the game engine the levels were created for.
7469
7470      Since 2.0.1, the game engine version is now directly stored
7471      in the level file (chunk "VERS"), so there is no need anymore
7472      to set the game version from the file version (except for old,
7473      pre-2.0 levels, where the game version is still taken from the
7474      file format version used to store the level -- see above). */
7475
7476   // player was faster than enemies in 1.0.0 and before
7477   if (level->file_version == FILE_VERSION_1_0)
7478     for (i = 0; i < MAX_PLAYERS; i++)
7479       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7480
7481   // default behaviour for EM style gems was "slippery" only in 2.0.1
7482   if (level->game_version == VERSION_IDENT(2,0,1,0))
7483     level->em_slippery_gems = TRUE;
7484
7485   // springs could be pushed over pits before (pre-release version) 2.2.0
7486   if (level->game_version < VERSION_IDENT(2,2,0,0))
7487     level->use_spring_bug = TRUE;
7488
7489   if (level->game_version < VERSION_IDENT(3,2,0,5))
7490   {
7491     // time orb caused limited time in endless time levels before 3.2.0-5
7492     level->use_time_orb_bug = TRUE;
7493
7494     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7495     level->block_snap_field = FALSE;
7496
7497     // extra time score was same value as time left score before 3.2.0-5
7498     level->extra_time_score = level->score[SC_TIME_BONUS];
7499   }
7500
7501   if (level->game_version < VERSION_IDENT(3,2,0,7))
7502   {
7503     // default behaviour for snapping was "not continuous" before 3.2.0-7
7504     level->continuous_snapping = FALSE;
7505   }
7506
7507   // only few elements were able to actively move into acid before 3.1.0
7508   // trigger settings did not exist before 3.1.0; set to default "any"
7509   if (level->game_version < VERSION_IDENT(3,1,0,0))
7510   {
7511     // correct "can move into acid" settings (all zero in old levels)
7512
7513     level->can_move_into_acid_bits = 0; // nothing can move into acid
7514     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7515
7516     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7517     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7518     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7519     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7520
7521     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7522       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7523
7524     // correct trigger settings (stored as zero == "none" in old levels)
7525
7526     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7527     {
7528       int element = EL_CUSTOM_START + i;
7529       struct ElementInfo *ei = &element_info[element];
7530
7531       for (j = 0; j < ei->num_change_pages; j++)
7532       {
7533         struct ElementChangeInfo *change = &ei->change_page[j];
7534
7535         change->trigger_player = CH_PLAYER_ANY;
7536         change->trigger_page = CH_PAGE_ANY;
7537       }
7538     }
7539   }
7540
7541   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7542   {
7543     int element = EL_CUSTOM_256;
7544     struct ElementInfo *ei = &element_info[element];
7545     struct ElementChangeInfo *change = &ei->change_page[0];
7546
7547     /* This is needed to fix a problem that was caused by a bugfix in function
7548        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7549        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7550        not replace walkable elements, but instead just placed the player on it,
7551        without placing the Sokoban field under the player). Unfortunately, this
7552        breaks "Snake Bite" style levels when the snake is halfway through a door
7553        that just closes (the snake head is still alive and can be moved in this
7554        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7555        player (without Sokoban element) which then gets killed as designed). */
7556
7557     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7558          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7559         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7560       change->target_element = EL_PLAYER_1;
7561   }
7562
7563   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7564   if (level->game_version < VERSION_IDENT(3,2,5,0))
7565   {
7566     /* This is needed to fix a problem that was caused by a bugfix in function
7567        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7568        corrects the behaviour when a custom element changes to another custom
7569        element with a higher element number that has change actions defined.
7570        Normally, only one change per frame is allowed for custom elements.
7571        Therefore, it is checked if a custom element already changed in the
7572        current frame; if it did, subsequent changes are suppressed.
7573        Unfortunately, this is only checked for element changes, but not for
7574        change actions, which are still executed. As the function above loops
7575        through all custom elements from lower to higher, an element change
7576        resulting in a lower CE number won't be checked again, while a target
7577        element with a higher number will also be checked, and potential change
7578        actions will get executed for this CE, too (which is wrong), while
7579        further changes are ignored (which is correct). As this bugfix breaks
7580        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7581        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7582        behaviour for existing levels and tapes that make use of this bug */
7583
7584     level->use_action_after_change_bug = TRUE;
7585   }
7586
7587   // not centering level after relocating player was default only in 3.2.3
7588   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7589     level->shifted_relocation = TRUE;
7590
7591   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7592   if (level->game_version < VERSION_IDENT(3,2,6,0))
7593     level->em_explodes_by_fire = TRUE;
7594
7595   // levels were solved by the first player entering an exit up to 4.1.0.0
7596   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7597     level->solved_by_one_player = TRUE;
7598
7599   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7600   if (level->game_version < VERSION_IDENT(4,1,1,1))
7601     level->use_life_bugs = TRUE;
7602
7603   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7604   if (level->game_version < VERSION_IDENT(4,1,1,1))
7605     level->sb_objects_needed = FALSE;
7606
7607   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7608   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7609     level->finish_dig_collect = FALSE;
7610
7611   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7612   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7613     level->keep_walkable_ce = TRUE;
7614 }
7615
7616 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7617 {
7618   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7619   int x, y;
7620
7621   // check if this level is (not) a Sokoban level
7622   for (y = 0; y < level->fieldy; y++)
7623     for (x = 0; x < level->fieldx; x++)
7624       if (!IS_SB_ELEMENT(Tile[x][y]))
7625         is_sokoban_level = FALSE;
7626
7627   if (is_sokoban_level)
7628   {
7629     // set special level settings for Sokoban levels
7630     SetLevelSettings_SB(level);
7631   }
7632 }
7633
7634 static void LoadLevel_InitSettings(struct LevelInfo *level)
7635 {
7636   // adjust level settings for (non-native) Sokoban-style levels
7637   LoadLevel_InitSettings_SB(level);
7638
7639   // rename levels with title "nameless level" or if renaming is forced
7640   if (leveldir_current->empty_level_name != NULL &&
7641       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7642        leveldir_current->force_level_name))
7643     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7644              leveldir_current->empty_level_name, level_nr);
7645 }
7646
7647 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7648 {
7649   int i, x, y;
7650
7651   // map elements that have changed in newer versions
7652   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7653                                                     level->game_version);
7654   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7655     for (x = 0; x < 3; x++)
7656       for (y = 0; y < 3; y++)
7657         level->yamyam_content[i].e[x][y] =
7658           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7659                                     level->game_version);
7660
7661 }
7662
7663 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7664 {
7665   int i, j;
7666
7667   // map custom element change events that have changed in newer versions
7668   // (these following values were accidentally changed in version 3.0.1)
7669   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7670   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7671   {
7672     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7673     {
7674       int element = EL_CUSTOM_START + i;
7675
7676       // order of checking and copying events to be mapped is important
7677       // (do not change the start and end value -- they are constant)
7678       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7679       {
7680         if (HAS_CHANGE_EVENT(element, j - 2))
7681         {
7682           SET_CHANGE_EVENT(element, j - 2, FALSE);
7683           SET_CHANGE_EVENT(element, j, TRUE);
7684         }
7685       }
7686
7687       // order of checking and copying events to be mapped is important
7688       // (do not change the start and end value -- they are constant)
7689       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7690       {
7691         if (HAS_CHANGE_EVENT(element, j - 1))
7692         {
7693           SET_CHANGE_EVENT(element, j - 1, FALSE);
7694           SET_CHANGE_EVENT(element, j, TRUE);
7695         }
7696       }
7697     }
7698   }
7699
7700   // initialize "can_change" field for old levels with only one change page
7701   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7702   {
7703     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7704     {
7705       int element = EL_CUSTOM_START + i;
7706
7707       if (CAN_CHANGE(element))
7708         element_info[element].change->can_change = TRUE;
7709     }
7710   }
7711
7712   // correct custom element values (for old levels without these options)
7713   if (level->game_version < VERSION_IDENT(3,1,1,0))
7714   {
7715     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7716     {
7717       int element = EL_CUSTOM_START + i;
7718       struct ElementInfo *ei = &element_info[element];
7719
7720       if (ei->access_direction == MV_NO_DIRECTION)
7721         ei->access_direction = MV_ALL_DIRECTIONS;
7722     }
7723   }
7724
7725   // correct custom element values (fix invalid values for all versions)
7726   if (1)
7727   {
7728     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7729     {
7730       int element = EL_CUSTOM_START + i;
7731       struct ElementInfo *ei = &element_info[element];
7732
7733       for (j = 0; j < ei->num_change_pages; j++)
7734       {
7735         struct ElementChangeInfo *change = &ei->change_page[j];
7736
7737         if (change->trigger_player == CH_PLAYER_NONE)
7738           change->trigger_player = CH_PLAYER_ANY;
7739
7740         if (change->trigger_side == CH_SIDE_NONE)
7741           change->trigger_side = CH_SIDE_ANY;
7742       }
7743     }
7744   }
7745
7746   // initialize "can_explode" field for old levels which did not store this
7747   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7748   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7749   {
7750     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7751     {
7752       int element = EL_CUSTOM_START + i;
7753
7754       if (EXPLODES_1X1_OLD(element))
7755         element_info[element].explosion_type = EXPLODES_1X1;
7756
7757       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7758                                              EXPLODES_SMASHED(element) ||
7759                                              EXPLODES_IMPACT(element)));
7760     }
7761   }
7762
7763   // correct previously hard-coded move delay values for maze runner style
7764   if (level->game_version < VERSION_IDENT(3,1,1,0))
7765   {
7766     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7767     {
7768       int element = EL_CUSTOM_START + i;
7769
7770       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7771       {
7772         // previously hard-coded and therefore ignored
7773         element_info[element].move_delay_fixed = 9;
7774         element_info[element].move_delay_random = 0;
7775       }
7776     }
7777   }
7778
7779   // set some other uninitialized values of custom elements in older levels
7780   if (level->game_version < VERSION_IDENT(3,1,0,0))
7781   {
7782     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7783     {
7784       int element = EL_CUSTOM_START + i;
7785
7786       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7787
7788       element_info[element].explosion_delay = 17;
7789       element_info[element].ignition_delay = 8;
7790     }
7791   }
7792
7793   // set mouse click change events to work for left/middle/right mouse button
7794   if (level->game_version < VERSION_IDENT(4,2,3,0))
7795   {
7796     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7797     {
7798       int element = EL_CUSTOM_START + i;
7799       struct ElementInfo *ei = &element_info[element];
7800
7801       for (j = 0; j < ei->num_change_pages; j++)
7802       {
7803         struct ElementChangeInfo *change = &ei->change_page[j];
7804
7805         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7806             change->has_event[CE_PRESSED_BY_MOUSE] ||
7807             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7808             change->has_event[CE_MOUSE_PRESSED_ON_X])
7809           change->trigger_side = CH_SIDE_ANY;
7810       }
7811     }
7812   }
7813 }
7814
7815 static void LoadLevel_InitElements(struct LevelInfo *level)
7816 {
7817   LoadLevel_InitStandardElements(level);
7818
7819   if (level->file_has_custom_elements)
7820     LoadLevel_InitCustomElements(level);
7821
7822   // initialize element properties for level editor etc.
7823   InitElementPropertiesEngine(level->game_version);
7824   InitElementPropertiesGfxElement();
7825 }
7826
7827 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7828 {
7829   int x, y;
7830
7831   // map elements that have changed in newer versions
7832   for (y = 0; y < level->fieldy; y++)
7833     for (x = 0; x < level->fieldx; x++)
7834       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7835                                                      level->game_version);
7836
7837   // clear unused playfield data (nicer if level gets resized in editor)
7838   for (x = 0; x < MAX_LEV_FIELDX; x++)
7839     for (y = 0; y < MAX_LEV_FIELDY; y++)
7840       if (x >= level->fieldx || y >= level->fieldy)
7841         level->field[x][y] = EL_EMPTY;
7842
7843   // copy elements to runtime playfield array
7844   for (x = 0; x < MAX_LEV_FIELDX; x++)
7845     for (y = 0; y < MAX_LEV_FIELDY; y++)
7846       Tile[x][y] = level->field[x][y];
7847
7848   // initialize level size variables for faster access
7849   lev_fieldx = level->fieldx;
7850   lev_fieldy = level->fieldy;
7851
7852   // determine border element for this level
7853   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7854     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7855   else
7856     SetBorderElement();
7857 }
7858
7859 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7860 {
7861   struct LevelFileInfo *level_file_info = &level->file_info;
7862
7863   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7864     CopyNativeLevel_RND_to_Native(level);
7865 }
7866
7867 static void LoadLevelTemplate_LoadAndInit(void)
7868 {
7869   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7870
7871   LoadLevel_InitVersion(&level_template);
7872   LoadLevel_InitElements(&level_template);
7873   LoadLevel_InitSettings(&level_template);
7874
7875   ActivateLevelTemplate();
7876 }
7877
7878 void LoadLevelTemplate(int nr)
7879 {
7880   if (!fileExists(getGlobalLevelTemplateFilename()))
7881   {
7882     Warn("no level template found for this level");
7883
7884     return;
7885   }
7886
7887   setLevelFileInfo(&level_template.file_info, nr);
7888
7889   LoadLevelTemplate_LoadAndInit();
7890 }
7891
7892 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7893 {
7894   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7895
7896   LoadLevelTemplate_LoadAndInit();
7897 }
7898
7899 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7900 {
7901   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7902
7903   if (level.use_custom_template)
7904   {
7905     if (network_level != NULL)
7906       LoadNetworkLevelTemplate(network_level);
7907     else
7908       LoadLevelTemplate(-1);
7909   }
7910
7911   LoadLevel_InitVersion(&level);
7912   LoadLevel_InitElements(&level);
7913   LoadLevel_InitPlayfield(&level);
7914   LoadLevel_InitSettings(&level);
7915
7916   LoadLevel_InitNativeEngines(&level);
7917 }
7918
7919 void LoadLevel(int nr)
7920 {
7921   SetLevelSetInfo(leveldir_current->identifier, nr);
7922
7923   setLevelFileInfo(&level.file_info, nr);
7924
7925   LoadLevel_LoadAndInit(NULL);
7926 }
7927
7928 void LoadLevelInfoOnly(int nr)
7929 {
7930   setLevelFileInfo(&level.file_info, nr);
7931
7932   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7933 }
7934
7935 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7936 {
7937   SetLevelSetInfo(network_level->leveldir_identifier,
7938                   network_level->file_info.nr);
7939
7940   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7941
7942   LoadLevel_LoadAndInit(network_level);
7943 }
7944
7945 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7946 {
7947   int chunk_size = 0;
7948
7949   chunk_size += putFileVersion(file, level->file_version);
7950   chunk_size += putFileVersion(file, level->game_version);
7951
7952   return chunk_size;
7953 }
7954
7955 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7956 {
7957   int chunk_size = 0;
7958
7959   chunk_size += putFile16BitBE(file, level->creation_date.year);
7960   chunk_size += putFile8Bit(file,    level->creation_date.month);
7961   chunk_size += putFile8Bit(file,    level->creation_date.day);
7962
7963   return chunk_size;
7964 }
7965
7966 #if ENABLE_HISTORIC_CHUNKS
7967 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7968 {
7969   int i, x, y;
7970
7971   putFile8Bit(file, level->fieldx);
7972   putFile8Bit(file, level->fieldy);
7973
7974   putFile16BitBE(file, level->time);
7975   putFile16BitBE(file, level->gems_needed);
7976
7977   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7978     putFile8Bit(file, level->name[i]);
7979
7980   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7981     putFile8Bit(file, level->score[i]);
7982
7983   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7984     for (y = 0; y < 3; y++)
7985       for (x = 0; x < 3; x++)
7986         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7987                            level->yamyam_content[i].e[x][y]));
7988   putFile8Bit(file, level->amoeba_speed);
7989   putFile8Bit(file, level->time_magic_wall);
7990   putFile8Bit(file, level->time_wheel);
7991   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7992                      level->amoeba_content));
7993   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7994   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7995   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7996   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7997
7998   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7999
8000   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8001   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8002   putFile32BitBE(file, level->can_move_into_acid_bits);
8003   putFile8Bit(file, level->dont_collide_with_bits);
8004
8005   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8006   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8007
8008   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8009   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8010   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8011
8012   putFile8Bit(file, level->game_engine_type);
8013
8014   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8015 }
8016 #endif
8017
8018 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8019 {
8020   int chunk_size = 0;
8021   int i;
8022
8023   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8024     chunk_size += putFile8Bit(file, level->name[i]);
8025
8026   return chunk_size;
8027 }
8028
8029 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8030 {
8031   int chunk_size = 0;
8032   int i;
8033
8034   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8035     chunk_size += putFile8Bit(file, level->author[i]);
8036
8037   return chunk_size;
8038 }
8039
8040 #if ENABLE_HISTORIC_CHUNKS
8041 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8042 {
8043   int chunk_size = 0;
8044   int x, y;
8045
8046   for (y = 0; y < level->fieldy; y++)
8047     for (x = 0; x < level->fieldx; x++)
8048       if (level->encoding_16bit_field)
8049         chunk_size += putFile16BitBE(file, level->field[x][y]);
8050       else
8051         chunk_size += putFile8Bit(file, level->field[x][y]);
8052
8053   return chunk_size;
8054 }
8055 #endif
8056
8057 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8058 {
8059   int chunk_size = 0;
8060   int x, y;
8061
8062   for (y = 0; y < level->fieldy; y++) 
8063     for (x = 0; x < level->fieldx; x++) 
8064       chunk_size += putFile16BitBE(file, level->field[x][y]);
8065
8066   return chunk_size;
8067 }
8068
8069 #if ENABLE_HISTORIC_CHUNKS
8070 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8071 {
8072   int i, x, y;
8073
8074   putFile8Bit(file, EL_YAMYAM);
8075   putFile8Bit(file, level->num_yamyam_contents);
8076   putFile8Bit(file, 0);
8077   putFile8Bit(file, 0);
8078
8079   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8080     for (y = 0; y < 3; y++)
8081       for (x = 0; x < 3; x++)
8082         if (level->encoding_16bit_field)
8083           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8084         else
8085           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8086 }
8087 #endif
8088
8089 #if ENABLE_HISTORIC_CHUNKS
8090 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8091 {
8092   int i, x, y;
8093   int num_contents, content_xsize, content_ysize;
8094   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8095
8096   if (element == EL_YAMYAM)
8097   {
8098     num_contents = level->num_yamyam_contents;
8099     content_xsize = 3;
8100     content_ysize = 3;
8101
8102     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8103       for (y = 0; y < 3; y++)
8104         for (x = 0; x < 3; x++)
8105           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8106   }
8107   else if (element == EL_BD_AMOEBA)
8108   {
8109     num_contents = 1;
8110     content_xsize = 1;
8111     content_ysize = 1;
8112
8113     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8114       for (y = 0; y < 3; y++)
8115         for (x = 0; x < 3; x++)
8116           content_array[i][x][y] = EL_EMPTY;
8117     content_array[0][0][0] = level->amoeba_content;
8118   }
8119   else
8120   {
8121     // chunk header already written -- write empty chunk data
8122     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8123
8124     Warn("cannot save content for element '%d'", element);
8125
8126     return;
8127   }
8128
8129   putFile16BitBE(file, element);
8130   putFile8Bit(file, num_contents);
8131   putFile8Bit(file, content_xsize);
8132   putFile8Bit(file, content_ysize);
8133
8134   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8135
8136   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8137     for (y = 0; y < 3; y++)
8138       for (x = 0; x < 3; x++)
8139         putFile16BitBE(file, content_array[i][x][y]);
8140 }
8141 #endif
8142
8143 #if ENABLE_HISTORIC_CHUNKS
8144 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8145 {
8146   int envelope_nr = element - EL_ENVELOPE_1;
8147   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8148   int chunk_size = 0;
8149   int i;
8150
8151   chunk_size += putFile16BitBE(file, element);
8152   chunk_size += putFile16BitBE(file, envelope_len);
8153   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8154   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8155
8156   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8157   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8158
8159   for (i = 0; i < envelope_len; i++)
8160     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8161
8162   return chunk_size;
8163 }
8164 #endif
8165
8166 #if ENABLE_HISTORIC_CHUNKS
8167 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8168                            int num_changed_custom_elements)
8169 {
8170   int i, check = 0;
8171
8172   putFile16BitBE(file, num_changed_custom_elements);
8173
8174   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8175   {
8176     int element = EL_CUSTOM_START + i;
8177
8178     struct ElementInfo *ei = &element_info[element];
8179
8180     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8181     {
8182       if (check < num_changed_custom_elements)
8183       {
8184         putFile16BitBE(file, element);
8185         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8186       }
8187
8188       check++;
8189     }
8190   }
8191
8192   if (check != num_changed_custom_elements)     // should not happen
8193     Warn("inconsistent number of custom element properties");
8194 }
8195 #endif
8196
8197 #if ENABLE_HISTORIC_CHUNKS
8198 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8199                            int num_changed_custom_elements)
8200 {
8201   int i, check = 0;
8202
8203   putFile16BitBE(file, num_changed_custom_elements);
8204
8205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8206   {
8207     int element = EL_CUSTOM_START + i;
8208
8209     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8210     {
8211       if (check < num_changed_custom_elements)
8212       {
8213         putFile16BitBE(file, element);
8214         putFile16BitBE(file, element_info[element].change->target_element);
8215       }
8216
8217       check++;
8218     }
8219   }
8220
8221   if (check != num_changed_custom_elements)     // should not happen
8222     Warn("inconsistent number of custom target elements");
8223 }
8224 #endif
8225
8226 #if ENABLE_HISTORIC_CHUNKS
8227 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8228                            int num_changed_custom_elements)
8229 {
8230   int i, j, x, y, check = 0;
8231
8232   putFile16BitBE(file, num_changed_custom_elements);
8233
8234   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8235   {
8236     int element = EL_CUSTOM_START + i;
8237     struct ElementInfo *ei = &element_info[element];
8238
8239     if (ei->modified_settings)
8240     {
8241       if (check < num_changed_custom_elements)
8242       {
8243         putFile16BitBE(file, element);
8244
8245         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8246           putFile8Bit(file, ei->description[j]);
8247
8248         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8249
8250         // some free bytes for future properties and padding
8251         WriteUnusedBytesToFile(file, 7);
8252
8253         putFile8Bit(file, ei->use_gfx_element);
8254         putFile16BitBE(file, ei->gfx_element_initial);
8255
8256         putFile8Bit(file, ei->collect_score_initial);
8257         putFile8Bit(file, ei->collect_count_initial);
8258
8259         putFile16BitBE(file, ei->push_delay_fixed);
8260         putFile16BitBE(file, ei->push_delay_random);
8261         putFile16BitBE(file, ei->move_delay_fixed);
8262         putFile16BitBE(file, ei->move_delay_random);
8263
8264         putFile16BitBE(file, ei->move_pattern);
8265         putFile8Bit(file, ei->move_direction_initial);
8266         putFile8Bit(file, ei->move_stepsize);
8267
8268         for (y = 0; y < 3; y++)
8269           for (x = 0; x < 3; x++)
8270             putFile16BitBE(file, ei->content.e[x][y]);
8271
8272         putFile32BitBE(file, ei->change->events);
8273
8274         putFile16BitBE(file, ei->change->target_element);
8275
8276         putFile16BitBE(file, ei->change->delay_fixed);
8277         putFile16BitBE(file, ei->change->delay_random);
8278         putFile16BitBE(file, ei->change->delay_frames);
8279
8280         putFile16BitBE(file, ei->change->initial_trigger_element);
8281
8282         putFile8Bit(file, ei->change->explode);
8283         putFile8Bit(file, ei->change->use_target_content);
8284         putFile8Bit(file, ei->change->only_if_complete);
8285         putFile8Bit(file, ei->change->use_random_replace);
8286
8287         putFile8Bit(file, ei->change->random_percentage);
8288         putFile8Bit(file, ei->change->replace_when);
8289
8290         for (y = 0; y < 3; y++)
8291           for (x = 0; x < 3; x++)
8292             putFile16BitBE(file, ei->change->content.e[x][y]);
8293
8294         putFile8Bit(file, ei->slippery_type);
8295
8296         // some free bytes for future properties and padding
8297         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8298       }
8299
8300       check++;
8301     }
8302   }
8303
8304   if (check != num_changed_custom_elements)     // should not happen
8305     Warn("inconsistent number of custom element properties");
8306 }
8307 #endif
8308
8309 #if ENABLE_HISTORIC_CHUNKS
8310 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8311 {
8312   struct ElementInfo *ei = &element_info[element];
8313   int i, j, x, y;
8314
8315   // ---------- custom element base property values (96 bytes) ----------------
8316
8317   putFile16BitBE(file, element);
8318
8319   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8320     putFile8Bit(file, ei->description[i]);
8321
8322   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8323
8324   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8325
8326   putFile8Bit(file, ei->num_change_pages);
8327
8328   putFile16BitBE(file, ei->ce_value_fixed_initial);
8329   putFile16BitBE(file, ei->ce_value_random_initial);
8330   putFile8Bit(file, ei->use_last_ce_value);
8331
8332   putFile8Bit(file, ei->use_gfx_element);
8333   putFile16BitBE(file, ei->gfx_element_initial);
8334
8335   putFile8Bit(file, ei->collect_score_initial);
8336   putFile8Bit(file, ei->collect_count_initial);
8337
8338   putFile8Bit(file, ei->drop_delay_fixed);
8339   putFile8Bit(file, ei->push_delay_fixed);
8340   putFile8Bit(file, ei->drop_delay_random);
8341   putFile8Bit(file, ei->push_delay_random);
8342   putFile16BitBE(file, ei->move_delay_fixed);
8343   putFile16BitBE(file, ei->move_delay_random);
8344
8345   // bits 0 - 15 of "move_pattern" ...
8346   putFile16BitBE(file, ei->move_pattern & 0xffff);
8347   putFile8Bit(file, ei->move_direction_initial);
8348   putFile8Bit(file, ei->move_stepsize);
8349
8350   putFile8Bit(file, ei->slippery_type);
8351
8352   for (y = 0; y < 3; y++)
8353     for (x = 0; x < 3; x++)
8354       putFile16BitBE(file, ei->content.e[x][y]);
8355
8356   putFile16BitBE(file, ei->move_enter_element);
8357   putFile16BitBE(file, ei->move_leave_element);
8358   putFile8Bit(file, ei->move_leave_type);
8359
8360   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8361   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8362
8363   putFile8Bit(file, ei->access_direction);
8364
8365   putFile8Bit(file, ei->explosion_delay);
8366   putFile8Bit(file, ei->ignition_delay);
8367   putFile8Bit(file, ei->explosion_type);
8368
8369   // some free bytes for future custom property values and padding
8370   WriteUnusedBytesToFile(file, 1);
8371
8372   // ---------- change page property values (48 bytes) ------------------------
8373
8374   for (i = 0; i < ei->num_change_pages; i++)
8375   {
8376     struct ElementChangeInfo *change = &ei->change_page[i];
8377     unsigned int event_bits;
8378
8379     // bits 0 - 31 of "has_event[]" ...
8380     event_bits = 0;
8381     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8382       if (change->has_event[j])
8383         event_bits |= (1u << j);
8384     putFile32BitBE(file, event_bits);
8385
8386     putFile16BitBE(file, change->target_element);
8387
8388     putFile16BitBE(file, change->delay_fixed);
8389     putFile16BitBE(file, change->delay_random);
8390     putFile16BitBE(file, change->delay_frames);
8391
8392     putFile16BitBE(file, change->initial_trigger_element);
8393
8394     putFile8Bit(file, change->explode);
8395     putFile8Bit(file, change->use_target_content);
8396     putFile8Bit(file, change->only_if_complete);
8397     putFile8Bit(file, change->use_random_replace);
8398
8399     putFile8Bit(file, change->random_percentage);
8400     putFile8Bit(file, change->replace_when);
8401
8402     for (y = 0; y < 3; y++)
8403       for (x = 0; x < 3; x++)
8404         putFile16BitBE(file, change->target_content.e[x][y]);
8405
8406     putFile8Bit(file, change->can_change);
8407
8408     putFile8Bit(file, change->trigger_side);
8409
8410     putFile8Bit(file, change->trigger_player);
8411     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8412                        log_2(change->trigger_page)));
8413
8414     putFile8Bit(file, change->has_action);
8415     putFile8Bit(file, change->action_type);
8416     putFile8Bit(file, change->action_mode);
8417     putFile16BitBE(file, change->action_arg);
8418
8419     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8420     event_bits = 0;
8421     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8422       if (change->has_event[j])
8423         event_bits |= (1u << (j - 32));
8424     putFile8Bit(file, event_bits);
8425   }
8426 }
8427 #endif
8428
8429 #if ENABLE_HISTORIC_CHUNKS
8430 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8431 {
8432   struct ElementInfo *ei = &element_info[element];
8433   struct ElementGroupInfo *group = ei->group;
8434   int i;
8435
8436   putFile16BitBE(file, element);
8437
8438   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8439     putFile8Bit(file, ei->description[i]);
8440
8441   putFile8Bit(file, group->num_elements);
8442
8443   putFile8Bit(file, ei->use_gfx_element);
8444   putFile16BitBE(file, ei->gfx_element_initial);
8445
8446   putFile8Bit(file, group->choice_mode);
8447
8448   // some free bytes for future values and padding
8449   WriteUnusedBytesToFile(file, 3);
8450
8451   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8452     putFile16BitBE(file, group->element[i]);
8453 }
8454 #endif
8455
8456 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8457                                 boolean write_element)
8458 {
8459   int save_type = entry->save_type;
8460   int data_type = entry->data_type;
8461   int conf_type = entry->conf_type;
8462   int byte_mask = conf_type & CONF_MASK_BYTES;
8463   int element = entry->element;
8464   int default_value = entry->default_value;
8465   int num_bytes = 0;
8466   boolean modified = FALSE;
8467
8468   if (byte_mask != CONF_MASK_MULTI_BYTES)
8469   {
8470     void *value_ptr = entry->value;
8471     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8472                  *(int *)value_ptr);
8473
8474     // check if any settings have been modified before saving them
8475     if (value != default_value)
8476       modified = TRUE;
8477
8478     // do not save if explicitly told or if unmodified default settings
8479     if ((save_type == SAVE_CONF_NEVER) ||
8480         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8481       return 0;
8482
8483     if (write_element)
8484       num_bytes += putFile16BitBE(file, element);
8485
8486     num_bytes += putFile8Bit(file, conf_type);
8487     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8488                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8489                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8490                   0);
8491   }
8492   else if (data_type == TYPE_STRING)
8493   {
8494     char *default_string = entry->default_string;
8495     char *string = (char *)(entry->value);
8496     int string_length = strlen(string);
8497     int i;
8498
8499     // check if any settings have been modified before saving them
8500     if (!strEqual(string, default_string))
8501       modified = TRUE;
8502
8503     // do not save if explicitly told or if unmodified default settings
8504     if ((save_type == SAVE_CONF_NEVER) ||
8505         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8506       return 0;
8507
8508     if (write_element)
8509       num_bytes += putFile16BitBE(file, element);
8510
8511     num_bytes += putFile8Bit(file, conf_type);
8512     num_bytes += putFile16BitBE(file, string_length);
8513
8514     for (i = 0; i < string_length; i++)
8515       num_bytes += putFile8Bit(file, string[i]);
8516   }
8517   else if (data_type == TYPE_ELEMENT_LIST)
8518   {
8519     int *element_array = (int *)(entry->value);
8520     int num_elements = *(int *)(entry->num_entities);
8521     int i;
8522
8523     // check if any settings have been modified before saving them
8524     for (i = 0; i < num_elements; i++)
8525       if (element_array[i] != default_value)
8526         modified = TRUE;
8527
8528     // do not save if explicitly told or if unmodified default settings
8529     if ((save_type == SAVE_CONF_NEVER) ||
8530         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8531       return 0;
8532
8533     if (write_element)
8534       num_bytes += putFile16BitBE(file, element);
8535
8536     num_bytes += putFile8Bit(file, conf_type);
8537     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8538
8539     for (i = 0; i < num_elements; i++)
8540       num_bytes += putFile16BitBE(file, element_array[i]);
8541   }
8542   else if (data_type == TYPE_CONTENT_LIST)
8543   {
8544     struct Content *content = (struct Content *)(entry->value);
8545     int num_contents = *(int *)(entry->num_entities);
8546     int i, x, y;
8547
8548     // check if any settings have been modified before saving them
8549     for (i = 0; i < num_contents; i++)
8550       for (y = 0; y < 3; y++)
8551         for (x = 0; x < 3; x++)
8552           if (content[i].e[x][y] != default_value)
8553             modified = TRUE;
8554
8555     // do not save if explicitly told or if unmodified default settings
8556     if ((save_type == SAVE_CONF_NEVER) ||
8557         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8558       return 0;
8559
8560     if (write_element)
8561       num_bytes += putFile16BitBE(file, element);
8562
8563     num_bytes += putFile8Bit(file, conf_type);
8564     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8565
8566     for (i = 0; i < num_contents; i++)
8567       for (y = 0; y < 3; y++)
8568         for (x = 0; x < 3; x++)
8569           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8570   }
8571
8572   return num_bytes;
8573 }
8574
8575 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8576 {
8577   int chunk_size = 0;
8578   int i;
8579
8580   li = *level;          // copy level data into temporary buffer
8581
8582   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8583     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8584
8585   return chunk_size;
8586 }
8587
8588 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8589 {
8590   int chunk_size = 0;
8591   int i;
8592
8593   li = *level;          // copy level data into temporary buffer
8594
8595   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8596     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8597
8598   return chunk_size;
8599 }
8600
8601 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8602 {
8603   int envelope_nr = element - EL_ENVELOPE_1;
8604   int chunk_size = 0;
8605   int i;
8606
8607   chunk_size += putFile16BitBE(file, element);
8608
8609   // copy envelope data into temporary buffer
8610   xx_envelope = level->envelope[envelope_nr];
8611
8612   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8613     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8614
8615   return chunk_size;
8616 }
8617
8618 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8619 {
8620   struct ElementInfo *ei = &element_info[element];
8621   int chunk_size = 0;
8622   int i, j;
8623
8624   chunk_size += putFile16BitBE(file, element);
8625
8626   xx_ei = *ei;          // copy element data into temporary buffer
8627
8628   // set default description string for this specific element
8629   strcpy(xx_default_description, getDefaultElementDescription(ei));
8630
8631   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8632     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8633
8634   for (i = 0; i < ei->num_change_pages; i++)
8635   {
8636     struct ElementChangeInfo *change = &ei->change_page[i];
8637
8638     xx_current_change_page = i;
8639
8640     xx_change = *change;        // copy change data into temporary buffer
8641
8642     resetEventBits();
8643     setEventBitsFromEventFlags(change);
8644
8645     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8646       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8647                                          FALSE);
8648   }
8649
8650   return chunk_size;
8651 }
8652
8653 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8654 {
8655   struct ElementInfo *ei = &element_info[element];
8656   struct ElementGroupInfo *group = ei->group;
8657   int chunk_size = 0;
8658   int i;
8659
8660   chunk_size += putFile16BitBE(file, element);
8661
8662   xx_ei = *ei;          // copy element data into temporary buffer
8663   xx_group = *group;    // copy group data into temporary buffer
8664
8665   // set default description string for this specific element
8666   strcpy(xx_default_description, getDefaultElementDescription(ei));
8667
8668   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8669     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8670
8671   return chunk_size;
8672 }
8673
8674 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8675 {
8676   struct ElementInfo *ei = &element_info[element];
8677   int chunk_size = 0;
8678   int i;
8679
8680   chunk_size += putFile16BitBE(file, element);
8681
8682   xx_ei = *ei;          // copy element data into temporary buffer
8683
8684   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8685     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8686
8687   return chunk_size;
8688 }
8689
8690 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8691                                   boolean save_as_template)
8692 {
8693   int chunk_size;
8694   int i;
8695   FILE *file;
8696
8697   if (!(file = fopen(filename, MODE_WRITE)))
8698   {
8699     Warn("cannot save level file '%s'", filename);
8700
8701     return;
8702   }
8703
8704   level->file_version = FILE_VERSION_ACTUAL;
8705   level->game_version = GAME_VERSION_ACTUAL;
8706
8707   level->creation_date = getCurrentDate();
8708
8709   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8710   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8711
8712   chunk_size = SaveLevel_VERS(NULL, level);
8713   putFileChunkBE(file, "VERS", chunk_size);
8714   SaveLevel_VERS(file, level);
8715
8716   chunk_size = SaveLevel_DATE(NULL, level);
8717   putFileChunkBE(file, "DATE", chunk_size);
8718   SaveLevel_DATE(file, level);
8719
8720   chunk_size = SaveLevel_NAME(NULL, level);
8721   putFileChunkBE(file, "NAME", chunk_size);
8722   SaveLevel_NAME(file, level);
8723
8724   chunk_size = SaveLevel_AUTH(NULL, level);
8725   putFileChunkBE(file, "AUTH", chunk_size);
8726   SaveLevel_AUTH(file, level);
8727
8728   chunk_size = SaveLevel_INFO(NULL, level);
8729   putFileChunkBE(file, "INFO", chunk_size);
8730   SaveLevel_INFO(file, level);
8731
8732   chunk_size = SaveLevel_BODY(NULL, level);
8733   putFileChunkBE(file, "BODY", chunk_size);
8734   SaveLevel_BODY(file, level);
8735
8736   chunk_size = SaveLevel_ELEM(NULL, level);
8737   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8738   {
8739     putFileChunkBE(file, "ELEM", chunk_size);
8740     SaveLevel_ELEM(file, level);
8741   }
8742
8743   for (i = 0; i < NUM_ENVELOPES; i++)
8744   {
8745     int element = EL_ENVELOPE_1 + i;
8746
8747     chunk_size = SaveLevel_NOTE(NULL, level, element);
8748     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8749     {
8750       putFileChunkBE(file, "NOTE", chunk_size);
8751       SaveLevel_NOTE(file, level, element);
8752     }
8753   }
8754
8755   // if not using template level, check for non-default custom/group elements
8756   if (!level->use_custom_template || save_as_template)
8757   {
8758     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8759     {
8760       int element = EL_CUSTOM_START + i;
8761
8762       chunk_size = SaveLevel_CUSX(NULL, level, element);
8763       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8764       {
8765         putFileChunkBE(file, "CUSX", chunk_size);
8766         SaveLevel_CUSX(file, level, element);
8767       }
8768     }
8769
8770     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8771     {
8772       int element = EL_GROUP_START + i;
8773
8774       chunk_size = SaveLevel_GRPX(NULL, level, element);
8775       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8776       {
8777         putFileChunkBE(file, "GRPX", chunk_size);
8778         SaveLevel_GRPX(file, level, element);
8779       }
8780     }
8781
8782     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8783     {
8784       int element = GET_EMPTY_ELEMENT(i);
8785
8786       chunk_size = SaveLevel_EMPX(NULL, level, element);
8787       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8788       {
8789         putFileChunkBE(file, "EMPX", chunk_size);
8790         SaveLevel_EMPX(file, level, element);
8791       }
8792     }
8793   }
8794
8795   fclose(file);
8796
8797   SetFilePermissions(filename, PERMS_PRIVATE);
8798 }
8799
8800 void SaveLevel(int nr)
8801 {
8802   char *filename = getDefaultLevelFilename(nr);
8803
8804   SaveLevelFromFilename(&level, filename, FALSE);
8805 }
8806
8807 void SaveLevelTemplate(void)
8808 {
8809   char *filename = getLocalLevelTemplateFilename();
8810
8811   SaveLevelFromFilename(&level, filename, TRUE);
8812 }
8813
8814 boolean SaveLevelChecked(int nr)
8815 {
8816   char *filename = getDefaultLevelFilename(nr);
8817   boolean new_level = !fileExists(filename);
8818   boolean level_saved = FALSE;
8819
8820   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8821   {
8822     SaveLevel(nr);
8823
8824     if (new_level)
8825       Request("Level saved!", REQ_CONFIRM);
8826
8827     level_saved = TRUE;
8828   }
8829
8830   return level_saved;
8831 }
8832
8833 void DumpLevel(struct LevelInfo *level)
8834 {
8835   if (level->no_level_file || level->no_valid_file)
8836   {
8837     Warn("cannot dump -- no valid level file found");
8838
8839     return;
8840   }
8841
8842   PrintLine("-", 79);
8843   Print("Level xxx (file version %08d, game version %08d)\n",
8844         level->file_version, level->game_version);
8845   PrintLine("-", 79);
8846
8847   Print("Level author: '%s'\n", level->author);
8848   Print("Level title:  '%s'\n", level->name);
8849   Print("\n");
8850   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8851   Print("\n");
8852   Print("Level time:  %d seconds\n", level->time);
8853   Print("Gems needed: %d\n", level->gems_needed);
8854   Print("\n");
8855   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8856   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8857   Print("Time for light:      %d seconds\n", level->time_light);
8858   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8859   Print("\n");
8860   Print("Amoeba speed: %d\n", level->amoeba_speed);
8861   Print("\n");
8862
8863   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8864   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8865   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8866   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8867   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8868   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8869
8870   if (options.debug)
8871   {
8872     int i, j;
8873
8874     for (i = 0; i < NUM_ENVELOPES; i++)
8875     {
8876       char *text = level->envelope[i].text;
8877       int text_len = strlen(text);
8878       boolean has_text = FALSE;
8879
8880       for (j = 0; j < text_len; j++)
8881         if (text[j] != ' ' && text[j] != '\n')
8882           has_text = TRUE;
8883
8884       if (has_text)
8885       {
8886         Print("\n");
8887         Print("Envelope %d:\n'%s'\n", i + 1, text);
8888       }
8889     }
8890   }
8891
8892   PrintLine("-", 79);
8893 }
8894
8895 void DumpLevels(void)
8896 {
8897   static LevelDirTree *dumplevel_leveldir = NULL;
8898
8899   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8900                                                  global.dumplevel_leveldir);
8901
8902   if (dumplevel_leveldir == NULL)
8903     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8904
8905   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8906       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8907     Fail("no such level number: %d", global.dumplevel_level_nr);
8908
8909   leveldir_current = dumplevel_leveldir;
8910
8911   LoadLevel(global.dumplevel_level_nr);
8912   DumpLevel(&level);
8913
8914   CloseAllAndExit(0);
8915 }
8916
8917
8918 // ============================================================================
8919 // tape file functions
8920 // ============================================================================
8921
8922 static void setTapeInfoToDefaults(void)
8923 {
8924   int i;
8925
8926   // always start with reliable default values (empty tape)
8927   TapeErase();
8928
8929   // default values (also for pre-1.2 tapes) with only the first player
8930   tape.player_participates[0] = TRUE;
8931   for (i = 1; i < MAX_PLAYERS; i++)
8932     tape.player_participates[i] = FALSE;
8933
8934   // at least one (default: the first) player participates in every tape
8935   tape.num_participating_players = 1;
8936
8937   tape.property_bits = TAPE_PROPERTY_NONE;
8938
8939   tape.level_nr = level_nr;
8940   tape.counter = 0;
8941   tape.changed = FALSE;
8942   tape.solved = FALSE;
8943
8944   tape.recording = FALSE;
8945   tape.playing = FALSE;
8946   tape.pausing = FALSE;
8947
8948   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8949   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8950
8951   tape.no_info_chunk = TRUE;
8952   tape.no_valid_file = FALSE;
8953 }
8954
8955 static int getTapePosSize(struct TapeInfo *tape)
8956 {
8957   int tape_pos_size = 0;
8958
8959   if (tape->use_key_actions)
8960     tape_pos_size += tape->num_participating_players;
8961
8962   if (tape->use_mouse_actions)
8963     tape_pos_size += 3;         // x and y position and mouse button mask
8964
8965   tape_pos_size += 1;           // tape action delay value
8966
8967   return tape_pos_size;
8968 }
8969
8970 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8971 {
8972   tape->use_key_actions = FALSE;
8973   tape->use_mouse_actions = FALSE;
8974
8975   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8976     tape->use_key_actions = TRUE;
8977
8978   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8979     tape->use_mouse_actions = TRUE;
8980 }
8981
8982 static int getTapeActionValue(struct TapeInfo *tape)
8983 {
8984   return (tape->use_key_actions &&
8985           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8986           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8987           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8988           TAPE_ACTIONS_DEFAULT);
8989 }
8990
8991 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8992 {
8993   tape->file_version = getFileVersion(file);
8994   tape->game_version = getFileVersion(file);
8995
8996   return chunk_size;
8997 }
8998
8999 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9000 {
9001   int i;
9002
9003   tape->random_seed = getFile32BitBE(file);
9004   tape->date        = getFile32BitBE(file);
9005   tape->length      = getFile32BitBE(file);
9006
9007   // read header fields that are new since version 1.2
9008   if (tape->file_version >= FILE_VERSION_1_2)
9009   {
9010     byte store_participating_players = getFile8Bit(file);
9011     int engine_version;
9012
9013     // since version 1.2, tapes store which players participate in the tape
9014     tape->num_participating_players = 0;
9015     for (i = 0; i < MAX_PLAYERS; i++)
9016     {
9017       tape->player_participates[i] = FALSE;
9018
9019       if (store_participating_players & (1 << i))
9020       {
9021         tape->player_participates[i] = TRUE;
9022         tape->num_participating_players++;
9023       }
9024     }
9025
9026     setTapeActionFlags(tape, getFile8Bit(file));
9027
9028     tape->property_bits = getFile8Bit(file);
9029     tape->solved = getFile8Bit(file);
9030
9031     engine_version = getFileVersion(file);
9032     if (engine_version > 0)
9033       tape->engine_version = engine_version;
9034     else
9035       tape->engine_version = tape->game_version;
9036   }
9037
9038   return chunk_size;
9039 }
9040
9041 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9042 {
9043   tape->scr_fieldx = getFile8Bit(file);
9044   tape->scr_fieldy = getFile8Bit(file);
9045
9046   return chunk_size;
9047 }
9048
9049 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9050 {
9051   char *level_identifier = NULL;
9052   int level_identifier_size;
9053   int i;
9054
9055   tape->no_info_chunk = FALSE;
9056
9057   level_identifier_size = getFile16BitBE(file);
9058
9059   level_identifier = checked_malloc(level_identifier_size);
9060
9061   for (i = 0; i < level_identifier_size; i++)
9062     level_identifier[i] = getFile8Bit(file);
9063
9064   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9065   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9066
9067   checked_free(level_identifier);
9068
9069   tape->level_nr = getFile16BitBE(file);
9070
9071   chunk_size = 2 + level_identifier_size + 2;
9072
9073   return chunk_size;
9074 }
9075
9076 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9077 {
9078   int i, j;
9079   int tape_pos_size = getTapePosSize(tape);
9080   int chunk_size_expected = tape_pos_size * tape->length;
9081
9082   if (chunk_size_expected != chunk_size)
9083   {
9084     ReadUnusedBytesFromFile(file, chunk_size);
9085     return chunk_size_expected;
9086   }
9087
9088   for (i = 0; i < tape->length; i++)
9089   {
9090     if (i >= MAX_TAPE_LEN)
9091     {
9092       Warn("tape truncated -- size exceeds maximum tape size %d",
9093             MAX_TAPE_LEN);
9094
9095       // tape too large; read and ignore remaining tape data from this chunk
9096       for (;i < tape->length; i++)
9097         ReadUnusedBytesFromFile(file, tape_pos_size);
9098
9099       break;
9100     }
9101
9102     if (tape->use_key_actions)
9103     {
9104       for (j = 0; j < MAX_PLAYERS; j++)
9105       {
9106         tape->pos[i].action[j] = MV_NONE;
9107
9108         if (tape->player_participates[j])
9109           tape->pos[i].action[j] = getFile8Bit(file);
9110       }
9111     }
9112
9113     if (tape->use_mouse_actions)
9114     {
9115       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9116       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9117       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9118     }
9119
9120     tape->pos[i].delay = getFile8Bit(file);
9121
9122     if (tape->file_version == FILE_VERSION_1_0)
9123     {
9124       // eliminate possible diagonal moves in old tapes
9125       // this is only for backward compatibility
9126
9127       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9128       byte action = tape->pos[i].action[0];
9129       int k, num_moves = 0;
9130
9131       for (k = 0; k < 4; k++)
9132       {
9133         if (action & joy_dir[k])
9134         {
9135           tape->pos[i + num_moves].action[0] = joy_dir[k];
9136           if (num_moves > 0)
9137             tape->pos[i + num_moves].delay = 0;
9138           num_moves++;
9139         }
9140       }
9141
9142       if (num_moves > 1)
9143       {
9144         num_moves--;
9145         i += num_moves;
9146         tape->length += num_moves;
9147       }
9148     }
9149     else if (tape->file_version < FILE_VERSION_2_0)
9150     {
9151       // convert pre-2.0 tapes to new tape format
9152
9153       if (tape->pos[i].delay > 1)
9154       {
9155         // action part
9156         tape->pos[i + 1] = tape->pos[i];
9157         tape->pos[i + 1].delay = 1;
9158
9159         // delay part
9160         for (j = 0; j < MAX_PLAYERS; j++)
9161           tape->pos[i].action[j] = MV_NONE;
9162         tape->pos[i].delay--;
9163
9164         i++;
9165         tape->length++;
9166       }
9167     }
9168
9169     if (checkEndOfFile(file))
9170       break;
9171   }
9172
9173   if (i != tape->length)
9174     chunk_size = tape_pos_size * i;
9175
9176   return chunk_size;
9177 }
9178
9179 static void LoadTape_SokobanSolution(char *filename)
9180 {
9181   File *file;
9182   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9183
9184   if (!(file = openFile(filename, MODE_READ)))
9185   {
9186     tape.no_valid_file = TRUE;
9187
9188     return;
9189   }
9190
9191   while (!checkEndOfFile(file))
9192   {
9193     unsigned char c = getByteFromFile(file);
9194
9195     if (checkEndOfFile(file))
9196       break;
9197
9198     switch (c)
9199     {
9200       case 'u':
9201       case 'U':
9202         tape.pos[tape.length].action[0] = MV_UP;
9203         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9204         tape.length++;
9205         break;
9206
9207       case 'd':
9208       case 'D':
9209         tape.pos[tape.length].action[0] = MV_DOWN;
9210         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9211         tape.length++;
9212         break;
9213
9214       case 'l':
9215       case 'L':
9216         tape.pos[tape.length].action[0] = MV_LEFT;
9217         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9218         tape.length++;
9219         break;
9220
9221       case 'r':
9222       case 'R':
9223         tape.pos[tape.length].action[0] = MV_RIGHT;
9224         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9225         tape.length++;
9226         break;
9227
9228       case '\n':
9229       case '\r':
9230       case '\t':
9231       case ' ':
9232         // ignore white-space characters
9233         break;
9234
9235       default:
9236         tape.no_valid_file = TRUE;
9237
9238         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9239
9240         break;
9241     }
9242   }
9243
9244   closeFile(file);
9245
9246   if (tape.no_valid_file)
9247     return;
9248
9249   tape.length_frames  = GetTapeLengthFrames();
9250   tape.length_seconds = GetTapeLengthSeconds();
9251 }
9252
9253 void LoadTapeFromFilename(char *filename)
9254 {
9255   char cookie[MAX_LINE_LEN];
9256   char chunk_name[CHUNK_ID_LEN + 1];
9257   File *file;
9258   int chunk_size;
9259
9260   // always start with reliable default values
9261   setTapeInfoToDefaults();
9262
9263   if (strSuffix(filename, ".sln"))
9264   {
9265     LoadTape_SokobanSolution(filename);
9266
9267     return;
9268   }
9269
9270   if (!(file = openFile(filename, MODE_READ)))
9271   {
9272     tape.no_valid_file = TRUE;
9273
9274     return;
9275   }
9276
9277   getFileChunkBE(file, chunk_name, NULL);
9278   if (strEqual(chunk_name, "RND1"))
9279   {
9280     getFile32BitBE(file);               // not used
9281
9282     getFileChunkBE(file, chunk_name, NULL);
9283     if (!strEqual(chunk_name, "TAPE"))
9284     {
9285       tape.no_valid_file = TRUE;
9286
9287       Warn("unknown format of tape file '%s'", filename);
9288
9289       closeFile(file);
9290
9291       return;
9292     }
9293   }
9294   else  // check for pre-2.0 file format with cookie string
9295   {
9296     strcpy(cookie, chunk_name);
9297     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9298       cookie[4] = '\0';
9299     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9300       cookie[strlen(cookie) - 1] = '\0';
9301
9302     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9303     {
9304       tape.no_valid_file = TRUE;
9305
9306       Warn("unknown format of tape file '%s'", filename);
9307
9308       closeFile(file);
9309
9310       return;
9311     }
9312
9313     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9314     {
9315       tape.no_valid_file = TRUE;
9316
9317       Warn("unsupported version of tape file '%s'", filename);
9318
9319       closeFile(file);
9320
9321       return;
9322     }
9323
9324     // pre-2.0 tape files have no game version, so use file version here
9325     tape.game_version = tape.file_version;
9326   }
9327
9328   if (tape.file_version < FILE_VERSION_1_2)
9329   {
9330     // tape files from versions before 1.2.0 without chunk structure
9331     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9332     LoadTape_BODY(file, 2 * tape.length,      &tape);
9333   }
9334   else
9335   {
9336     static struct
9337     {
9338       char *name;
9339       int size;
9340       int (*loader)(File *, int, struct TapeInfo *);
9341     }
9342     chunk_info[] =
9343     {
9344       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9345       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9346       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9347       { "INFO", -1,                     LoadTape_INFO },
9348       { "BODY", -1,                     LoadTape_BODY },
9349       {  NULL,  0,                      NULL }
9350     };
9351
9352     while (getFileChunkBE(file, chunk_name, &chunk_size))
9353     {
9354       int i = 0;
9355
9356       while (chunk_info[i].name != NULL &&
9357              !strEqual(chunk_name, chunk_info[i].name))
9358         i++;
9359
9360       if (chunk_info[i].name == NULL)
9361       {
9362         Warn("unknown chunk '%s' in tape file '%s'",
9363               chunk_name, filename);
9364
9365         ReadUnusedBytesFromFile(file, chunk_size);
9366       }
9367       else if (chunk_info[i].size != -1 &&
9368                chunk_info[i].size != chunk_size)
9369       {
9370         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9371               chunk_size, chunk_name, filename);
9372
9373         ReadUnusedBytesFromFile(file, chunk_size);
9374       }
9375       else
9376       {
9377         // call function to load this tape chunk
9378         int chunk_size_expected =
9379           (chunk_info[i].loader)(file, chunk_size, &tape);
9380
9381         // the size of some chunks cannot be checked before reading other
9382         // chunks first (like "HEAD" and "BODY") that contain some header
9383         // information, so check them here
9384         if (chunk_size_expected != chunk_size)
9385         {
9386           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9387                 chunk_size, chunk_name, filename);
9388         }
9389       }
9390     }
9391   }
9392
9393   closeFile(file);
9394
9395   tape.length_frames  = GetTapeLengthFrames();
9396   tape.length_seconds = GetTapeLengthSeconds();
9397
9398 #if 0
9399   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9400         tape.file_version);
9401   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9402         tape.game_version);
9403   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9404         tape.engine_version);
9405 #endif
9406 }
9407
9408 void LoadTape(int nr)
9409 {
9410   char *filename = getTapeFilename(nr);
9411
9412   LoadTapeFromFilename(filename);
9413 }
9414
9415 void LoadSolutionTape(int nr)
9416 {
9417   char *filename = getSolutionTapeFilename(nr);
9418
9419   LoadTapeFromFilename(filename);
9420
9421   if (TAPE_IS_EMPTY(tape))
9422   {
9423     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9424         level.native_bd_level->replay != NULL)
9425       CopyNativeTape_BD_to_RND(&level);
9426     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9427         level.native_sp_level->demo.is_available)
9428       CopyNativeTape_SP_to_RND(&level);
9429   }
9430 }
9431
9432 void LoadScoreTape(char *score_tape_basename, int nr)
9433 {
9434   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9435
9436   LoadTapeFromFilename(filename);
9437 }
9438
9439 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9440 {
9441   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9442
9443   LoadTapeFromFilename(filename);
9444 }
9445
9446 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9447 {
9448   // chunk required for team mode tapes with non-default screen size
9449   return (tape->num_participating_players > 1 &&
9450           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9451            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9452 }
9453
9454 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9455 {
9456   putFileVersion(file, tape->file_version);
9457   putFileVersion(file, tape->game_version);
9458 }
9459
9460 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9461 {
9462   int i;
9463   byte store_participating_players = 0;
9464
9465   // set bits for participating players for compact storage
9466   for (i = 0; i < MAX_PLAYERS; i++)
9467     if (tape->player_participates[i])
9468       store_participating_players |= (1 << i);
9469
9470   putFile32BitBE(file, tape->random_seed);
9471   putFile32BitBE(file, tape->date);
9472   putFile32BitBE(file, tape->length);
9473
9474   putFile8Bit(file, store_participating_players);
9475
9476   putFile8Bit(file, getTapeActionValue(tape));
9477
9478   putFile8Bit(file, tape->property_bits);
9479   putFile8Bit(file, tape->solved);
9480
9481   putFileVersion(file, tape->engine_version);
9482 }
9483
9484 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9485 {
9486   putFile8Bit(file, tape->scr_fieldx);
9487   putFile8Bit(file, tape->scr_fieldy);
9488 }
9489
9490 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9491 {
9492   int level_identifier_size = strlen(tape->level_identifier) + 1;
9493   int i;
9494
9495   putFile16BitBE(file, level_identifier_size);
9496
9497   for (i = 0; i < level_identifier_size; i++)
9498     putFile8Bit(file, tape->level_identifier[i]);
9499
9500   putFile16BitBE(file, tape->level_nr);
9501 }
9502
9503 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9504 {
9505   int i, j;
9506
9507   for (i = 0; i < tape->length; i++)
9508   {
9509     if (tape->use_key_actions)
9510     {
9511       for (j = 0; j < MAX_PLAYERS; j++)
9512         if (tape->player_participates[j])
9513           putFile8Bit(file, tape->pos[i].action[j]);
9514     }
9515
9516     if (tape->use_mouse_actions)
9517     {
9518       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9519       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9520       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9521     }
9522
9523     putFile8Bit(file, tape->pos[i].delay);
9524   }
9525 }
9526
9527 void SaveTapeToFilename(char *filename)
9528 {
9529   FILE *file;
9530   int tape_pos_size;
9531   int info_chunk_size;
9532   int body_chunk_size;
9533
9534   if (!(file = fopen(filename, MODE_WRITE)))
9535   {
9536     Warn("cannot save level recording file '%s'", filename);
9537
9538     return;
9539   }
9540
9541   tape_pos_size = getTapePosSize(&tape);
9542
9543   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9544   body_chunk_size = tape_pos_size * tape.length;
9545
9546   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9547   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9548
9549   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9550   SaveTape_VERS(file, &tape);
9551
9552   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9553   SaveTape_HEAD(file, &tape);
9554
9555   if (checkSaveTape_SCRN(&tape))
9556   {
9557     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9558     SaveTape_SCRN(file, &tape);
9559   }
9560
9561   putFileChunkBE(file, "INFO", info_chunk_size);
9562   SaveTape_INFO(file, &tape);
9563
9564   putFileChunkBE(file, "BODY", body_chunk_size);
9565   SaveTape_BODY(file, &tape);
9566
9567   fclose(file);
9568
9569   SetFilePermissions(filename, PERMS_PRIVATE);
9570 }
9571
9572 static void SaveTapeExt(char *filename)
9573 {
9574   int i;
9575
9576   tape.file_version = FILE_VERSION_ACTUAL;
9577   tape.game_version = GAME_VERSION_ACTUAL;
9578
9579   tape.num_participating_players = 0;
9580
9581   // count number of participating players
9582   for (i = 0; i < MAX_PLAYERS; i++)
9583     if (tape.player_participates[i])
9584       tape.num_participating_players++;
9585
9586   SaveTapeToFilename(filename);
9587
9588   tape.changed = FALSE;
9589 }
9590
9591 void SaveTape(int nr)
9592 {
9593   char *filename = getTapeFilename(nr);
9594
9595   InitTapeDirectory(leveldir_current->subdir);
9596
9597   SaveTapeExt(filename);
9598 }
9599
9600 void SaveScoreTape(int nr)
9601 {
9602   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9603
9604   // used instead of "leveldir_current->subdir" (for network games)
9605   InitScoreTapeDirectory(levelset.identifier, nr);
9606
9607   SaveTapeExt(filename);
9608 }
9609
9610 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9611                                   unsigned int req_state_added)
9612 {
9613   char *filename = getTapeFilename(nr);
9614   boolean new_tape = !fileExists(filename);
9615   boolean tape_saved = FALSE;
9616
9617   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9618   {
9619     SaveTape(nr);
9620
9621     if (new_tape)
9622       Request(msg_saved, REQ_CONFIRM | req_state_added);
9623
9624     tape_saved = TRUE;
9625   }
9626
9627   return tape_saved;
9628 }
9629
9630 boolean SaveTapeChecked(int nr)
9631 {
9632   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9633 }
9634
9635 boolean SaveTapeChecked_LevelSolved(int nr)
9636 {
9637   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9638                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9639 }
9640
9641 void DumpTape(struct TapeInfo *tape)
9642 {
9643   int tape_frame_counter;
9644   int i, j;
9645
9646   if (tape->no_valid_file)
9647   {
9648     Warn("cannot dump -- no valid tape file found");
9649
9650     return;
9651   }
9652
9653   PrintLine("-", 79);
9654
9655   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9656         tape->level_nr, tape->file_version, tape->game_version);
9657   Print("                  (effective engine version %08d)\n",
9658         tape->engine_version);
9659   Print("Level series identifier: '%s'\n", tape->level_identifier);
9660
9661   Print("Solution tape: %s\n",
9662         tape->solved ? "yes" :
9663         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9664
9665   Print("Special tape properties: ");
9666   if (tape->property_bits == TAPE_PROPERTY_NONE)
9667     Print("[none]");
9668   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9669     Print("[em_random_bug]");
9670   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9671     Print("[game_speed]");
9672   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9673     Print("[pause]");
9674   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9675     Print("[single_step]");
9676   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9677     Print("[snapshot]");
9678   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9679     Print("[replayed]");
9680   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9681     Print("[tas_keys]");
9682   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9683     Print("[small_graphics]");
9684   Print("\n");
9685
9686   int year2 = tape->date / 10000;
9687   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9688   int month_index_raw = (tape->date / 100) % 100;
9689   int month_index = month_index_raw % 12;       // prevent invalid index
9690   int month = month_index + 1;
9691   int day = tape->date % 100;
9692
9693   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9694
9695   PrintLine("-", 79);
9696
9697   tape_frame_counter = 0;
9698
9699   for (i = 0; i < tape->length; i++)
9700   {
9701     if (i >= MAX_TAPE_LEN)
9702       break;
9703
9704     Print("%04d: ", i);
9705
9706     for (j = 0; j < MAX_PLAYERS; j++)
9707     {
9708       if (tape->player_participates[j])
9709       {
9710         int action = tape->pos[i].action[j];
9711
9712         Print("%d:%02x ", j, action);
9713         Print("[%c%c%c%c|%c%c] - ",
9714               (action & JOY_LEFT ? '<' : ' '),
9715               (action & JOY_RIGHT ? '>' : ' '),
9716               (action & JOY_UP ? '^' : ' '),
9717               (action & JOY_DOWN ? 'v' : ' '),
9718               (action & JOY_BUTTON_1 ? '1' : ' '),
9719               (action & JOY_BUTTON_2 ? '2' : ' '));
9720       }
9721     }
9722
9723     Print("(%03d) ", tape->pos[i].delay);
9724     Print("[%05d]\n", tape_frame_counter);
9725
9726     tape_frame_counter += tape->pos[i].delay;
9727   }
9728
9729   PrintLine("-", 79);
9730 }
9731
9732 void DumpTapes(void)
9733 {
9734   static LevelDirTree *dumptape_leveldir = NULL;
9735
9736   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9737                                                 global.dumptape_leveldir);
9738
9739   if (dumptape_leveldir == NULL)
9740     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9741
9742   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9743       global.dumptape_level_nr > dumptape_leveldir->last_level)
9744     Fail("no such level number: %d", global.dumptape_level_nr);
9745
9746   leveldir_current = dumptape_leveldir;
9747
9748   if (options.mytapes)
9749     LoadTape(global.dumptape_level_nr);
9750   else
9751     LoadSolutionTape(global.dumptape_level_nr);
9752
9753   DumpTape(&tape);
9754
9755   CloseAllAndExit(0);
9756 }
9757
9758
9759 // ============================================================================
9760 // score file functions
9761 // ============================================================================
9762
9763 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9764 {
9765   int i;
9766
9767   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9768   {
9769     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9770     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9771     scores->entry[i].score = 0;
9772     scores->entry[i].time = 0;
9773
9774     scores->entry[i].id = -1;
9775     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9776     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9777     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9778     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9779     strcpy(scores->entry[i].country_code, "??");
9780   }
9781
9782   scores->num_entries = 0;
9783   scores->last_added = -1;
9784   scores->last_added_local = -1;
9785
9786   scores->updated = FALSE;
9787   scores->uploaded = FALSE;
9788   scores->tape_downloaded = FALSE;
9789   scores->force_last_added = FALSE;
9790
9791   // The following values are intentionally not reset here:
9792   // - last_level_nr
9793   // - last_entry_nr
9794   // - next_level_nr
9795   // - continue_playing
9796   // - continue_on_return
9797 }
9798
9799 static void setScoreInfoToDefaults(void)
9800 {
9801   setScoreInfoToDefaultsExt(&scores);
9802 }
9803
9804 static void setServerScoreInfoToDefaults(void)
9805 {
9806   setScoreInfoToDefaultsExt(&server_scores);
9807 }
9808
9809 static void LoadScore_OLD(int nr)
9810 {
9811   int i;
9812   char *filename = getScoreFilename(nr);
9813   char cookie[MAX_LINE_LEN];
9814   char line[MAX_LINE_LEN];
9815   char *line_ptr;
9816   FILE *file;
9817
9818   if (!(file = fopen(filename, MODE_READ)))
9819     return;
9820
9821   // check file identifier
9822   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9823     cookie[0] = '\0';
9824   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9825     cookie[strlen(cookie) - 1] = '\0';
9826
9827   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9828   {
9829     Warn("unknown format of score file '%s'", filename);
9830
9831     fclose(file);
9832
9833     return;
9834   }
9835
9836   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9837   {
9838     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9839       Warn("fscanf() failed; %s", strerror(errno));
9840
9841     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9842       line[0] = '\0';
9843
9844     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9845       line[strlen(line) - 1] = '\0';
9846
9847     for (line_ptr = line; *line_ptr; line_ptr++)
9848     {
9849       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9850       {
9851         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9852         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9853         break;
9854       }
9855     }
9856   }
9857
9858   fclose(file);
9859 }
9860
9861 static void ConvertScore_OLD(void)
9862 {
9863   // only convert score to time for levels that rate playing time over score
9864   if (!level.rate_time_over_score)
9865     return;
9866
9867   // convert old score to playing time for score-less levels (like Supaplex)
9868   int time_final_max = 999;
9869   int i;
9870
9871   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9872   {
9873     int score = scores.entry[i].score;
9874
9875     if (score > 0 && score < time_final_max)
9876       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9877   }
9878 }
9879
9880 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9881 {
9882   scores->file_version = getFileVersion(file);
9883   scores->game_version = getFileVersion(file);
9884
9885   return chunk_size;
9886 }
9887
9888 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9889 {
9890   char *level_identifier = NULL;
9891   int level_identifier_size;
9892   int i;
9893
9894   level_identifier_size = getFile16BitBE(file);
9895
9896   level_identifier = checked_malloc(level_identifier_size);
9897
9898   for (i = 0; i < level_identifier_size; i++)
9899     level_identifier[i] = getFile8Bit(file);
9900
9901   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9902   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9903
9904   checked_free(level_identifier);
9905
9906   scores->level_nr = getFile16BitBE(file);
9907   scores->num_entries = getFile16BitBE(file);
9908
9909   chunk_size = 2 + level_identifier_size + 2 + 2;
9910
9911   return chunk_size;
9912 }
9913
9914 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9915 {
9916   int i, j;
9917
9918   for (i = 0; i < scores->num_entries; i++)
9919   {
9920     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9921       scores->entry[i].name[j] = getFile8Bit(file);
9922
9923     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9924   }
9925
9926   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9927
9928   return chunk_size;
9929 }
9930
9931 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9932 {
9933   int i;
9934
9935   for (i = 0; i < scores->num_entries; i++)
9936     scores->entry[i].score = getFile16BitBE(file);
9937
9938   chunk_size = scores->num_entries * 2;
9939
9940   return chunk_size;
9941 }
9942
9943 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9944 {
9945   int i;
9946
9947   for (i = 0; i < scores->num_entries; i++)
9948     scores->entry[i].score = getFile32BitBE(file);
9949
9950   chunk_size = scores->num_entries * 4;
9951
9952   return chunk_size;
9953 }
9954
9955 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9956 {
9957   int i;
9958
9959   for (i = 0; i < scores->num_entries; i++)
9960     scores->entry[i].time = getFile32BitBE(file);
9961
9962   chunk_size = scores->num_entries * 4;
9963
9964   return chunk_size;
9965 }
9966
9967 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9968 {
9969   int i, j;
9970
9971   for (i = 0; i < scores->num_entries; i++)
9972   {
9973     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9974       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9975
9976     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9977   }
9978
9979   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9980
9981   return chunk_size;
9982 }
9983
9984 void LoadScore(int nr)
9985 {
9986   char *filename = getScoreFilename(nr);
9987   char cookie[MAX_LINE_LEN];
9988   char chunk_name[CHUNK_ID_LEN + 1];
9989   int chunk_size;
9990   boolean old_score_file_format = FALSE;
9991   File *file;
9992
9993   // always start with reliable default values
9994   setScoreInfoToDefaults();
9995
9996   if (!(file = openFile(filename, MODE_READ)))
9997     return;
9998
9999   getFileChunkBE(file, chunk_name, NULL);
10000   if (strEqual(chunk_name, "RND1"))
10001   {
10002     getFile32BitBE(file);               // not used
10003
10004     getFileChunkBE(file, chunk_name, NULL);
10005     if (!strEqual(chunk_name, "SCOR"))
10006     {
10007       Warn("unknown format of score file '%s'", filename);
10008
10009       closeFile(file);
10010
10011       return;
10012     }
10013   }
10014   else  // check for old file format with cookie string
10015   {
10016     strcpy(cookie, chunk_name);
10017     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10018       cookie[4] = '\0';
10019     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10020       cookie[strlen(cookie) - 1] = '\0';
10021
10022     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10023     {
10024       Warn("unknown format of score file '%s'", filename);
10025
10026       closeFile(file);
10027
10028       return;
10029     }
10030
10031     old_score_file_format = TRUE;
10032   }
10033
10034   if (old_score_file_format)
10035   {
10036     // score files from versions before 4.2.4.0 without chunk structure
10037     LoadScore_OLD(nr);
10038
10039     // convert score to time, if possible (mainly for Supaplex levels)
10040     ConvertScore_OLD();
10041   }
10042   else
10043   {
10044     static struct
10045     {
10046       char *name;
10047       int size;
10048       int (*loader)(File *, int, struct ScoreInfo *);
10049     }
10050     chunk_info[] =
10051     {
10052       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10053       { "INFO", -1,                     LoadScore_INFO },
10054       { "NAME", -1,                     LoadScore_NAME },
10055       { "SCOR", -1,                     LoadScore_SCOR },
10056       { "SC4R", -1,                     LoadScore_SC4R },
10057       { "TIME", -1,                     LoadScore_TIME },
10058       { "TAPE", -1,                     LoadScore_TAPE },
10059
10060       {  NULL,  0,                      NULL }
10061     };
10062
10063     while (getFileChunkBE(file, chunk_name, &chunk_size))
10064     {
10065       int i = 0;
10066
10067       while (chunk_info[i].name != NULL &&
10068              !strEqual(chunk_name, chunk_info[i].name))
10069         i++;
10070
10071       if (chunk_info[i].name == NULL)
10072       {
10073         Warn("unknown chunk '%s' in score file '%s'",
10074               chunk_name, filename);
10075
10076         ReadUnusedBytesFromFile(file, chunk_size);
10077       }
10078       else if (chunk_info[i].size != -1 &&
10079                chunk_info[i].size != chunk_size)
10080       {
10081         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10082               chunk_size, chunk_name, filename);
10083
10084         ReadUnusedBytesFromFile(file, chunk_size);
10085       }
10086       else
10087       {
10088         // call function to load this score chunk
10089         int chunk_size_expected =
10090           (chunk_info[i].loader)(file, chunk_size, &scores);
10091
10092         // the size of some chunks cannot be checked before reading other
10093         // chunks first (like "HEAD" and "BODY") that contain some header
10094         // information, so check them here
10095         if (chunk_size_expected != chunk_size)
10096         {
10097           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10098                 chunk_size, chunk_name, filename);
10099         }
10100       }
10101     }
10102   }
10103
10104   closeFile(file);
10105 }
10106
10107 #if ENABLE_HISTORIC_CHUNKS
10108 void SaveScore_OLD(int nr)
10109 {
10110   int i;
10111   char *filename = getScoreFilename(nr);
10112   FILE *file;
10113
10114   // used instead of "leveldir_current->subdir" (for network games)
10115   InitScoreDirectory(levelset.identifier);
10116
10117   if (!(file = fopen(filename, MODE_WRITE)))
10118   {
10119     Warn("cannot save score for level %d", nr);
10120
10121     return;
10122   }
10123
10124   fprintf(file, "%s\n\n", SCORE_COOKIE);
10125
10126   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10127     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10128
10129   fclose(file);
10130
10131   SetFilePermissions(filename, PERMS_PRIVATE);
10132 }
10133 #endif
10134
10135 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10136 {
10137   putFileVersion(file, scores->file_version);
10138   putFileVersion(file, scores->game_version);
10139 }
10140
10141 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10142 {
10143   int level_identifier_size = strlen(scores->level_identifier) + 1;
10144   int i;
10145
10146   putFile16BitBE(file, level_identifier_size);
10147
10148   for (i = 0; i < level_identifier_size; i++)
10149     putFile8Bit(file, scores->level_identifier[i]);
10150
10151   putFile16BitBE(file, scores->level_nr);
10152   putFile16BitBE(file, scores->num_entries);
10153 }
10154
10155 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10156 {
10157   int i, j;
10158
10159   for (i = 0; i < scores->num_entries; i++)
10160   {
10161     int name_size = strlen(scores->entry[i].name);
10162
10163     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10164       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10165   }
10166 }
10167
10168 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10169 {
10170   int i;
10171
10172   for (i = 0; i < scores->num_entries; i++)
10173     putFile16BitBE(file, scores->entry[i].score);
10174 }
10175
10176 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10177 {
10178   int i;
10179
10180   for (i = 0; i < scores->num_entries; i++)
10181     putFile32BitBE(file, scores->entry[i].score);
10182 }
10183
10184 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10185 {
10186   int i;
10187
10188   for (i = 0; i < scores->num_entries; i++)
10189     putFile32BitBE(file, scores->entry[i].time);
10190 }
10191
10192 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10193 {
10194   int i, j;
10195
10196   for (i = 0; i < scores->num_entries; i++)
10197   {
10198     int size = strlen(scores->entry[i].tape_basename);
10199
10200     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10201       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10202   }
10203 }
10204
10205 static void SaveScoreToFilename(char *filename)
10206 {
10207   FILE *file;
10208   int info_chunk_size;
10209   int name_chunk_size;
10210   int scor_chunk_size;
10211   int sc4r_chunk_size;
10212   int time_chunk_size;
10213   int tape_chunk_size;
10214   boolean has_large_score_values;
10215   int i;
10216
10217   if (!(file = fopen(filename, MODE_WRITE)))
10218   {
10219     Warn("cannot save score file '%s'", filename);
10220
10221     return;
10222   }
10223
10224   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10225   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10226   scor_chunk_size = scores.num_entries * 2;
10227   sc4r_chunk_size = scores.num_entries * 4;
10228   time_chunk_size = scores.num_entries * 4;
10229   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10230
10231   has_large_score_values = FALSE;
10232   for (i = 0; i < scores.num_entries; i++)
10233     if (scores.entry[i].score > 0xffff)
10234       has_large_score_values = TRUE;
10235
10236   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10237   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10238
10239   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10240   SaveScore_VERS(file, &scores);
10241
10242   putFileChunkBE(file, "INFO", info_chunk_size);
10243   SaveScore_INFO(file, &scores);
10244
10245   putFileChunkBE(file, "NAME", name_chunk_size);
10246   SaveScore_NAME(file, &scores);
10247
10248   if (has_large_score_values)
10249   {
10250     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10251     SaveScore_SC4R(file, &scores);
10252   }
10253   else
10254   {
10255     putFileChunkBE(file, "SCOR", scor_chunk_size);
10256     SaveScore_SCOR(file, &scores);
10257   }
10258
10259   putFileChunkBE(file, "TIME", time_chunk_size);
10260   SaveScore_TIME(file, &scores);
10261
10262   putFileChunkBE(file, "TAPE", tape_chunk_size);
10263   SaveScore_TAPE(file, &scores);
10264
10265   fclose(file);
10266
10267   SetFilePermissions(filename, PERMS_PRIVATE);
10268 }
10269
10270 void SaveScore(int nr)
10271 {
10272   char *filename = getScoreFilename(nr);
10273   int i;
10274
10275   // used instead of "leveldir_current->subdir" (for network games)
10276   InitScoreDirectory(levelset.identifier);
10277
10278   scores.file_version = FILE_VERSION_ACTUAL;
10279   scores.game_version = GAME_VERSION_ACTUAL;
10280
10281   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10282   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10283   scores.level_nr = level_nr;
10284
10285   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10286     if (scores.entry[i].score == 0 &&
10287         scores.entry[i].time == 0 &&
10288         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10289       break;
10290
10291   scores.num_entries = i;
10292
10293   if (scores.num_entries == 0)
10294     return;
10295
10296   SaveScoreToFilename(filename);
10297 }
10298
10299 static void LoadServerScoreFromCache(int nr)
10300 {
10301   struct ScoreEntry score_entry;
10302   struct
10303   {
10304     void *value;
10305     boolean is_string;
10306     int string_size;
10307   }
10308   score_mapping[] =
10309   {
10310     { &score_entry.score,               FALSE,  0                       },
10311     { &score_entry.time,                FALSE,  0                       },
10312     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10313     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10314     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10315     { &score_entry.id,                  FALSE,  0                       },
10316     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10317     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10318     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10319     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10320
10321     { NULL,                             FALSE,  0                       }
10322   };
10323   char *filename = getScoreCacheFilename(nr);
10324   SetupFileHash *score_hash = loadSetupFileHash(filename);
10325   int i, j;
10326
10327   server_scores.num_entries = 0;
10328
10329   if (score_hash == NULL)
10330     return;
10331
10332   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10333   {
10334     score_entry = server_scores.entry[i];
10335
10336     for (j = 0; score_mapping[j].value != NULL; j++)
10337     {
10338       char token[10];
10339
10340       sprintf(token, "%02d.%d", i, j);
10341
10342       char *value = getHashEntry(score_hash, token);
10343
10344       if (value == NULL)
10345         continue;
10346
10347       if (score_mapping[j].is_string)
10348       {
10349         char *score_value = (char *)score_mapping[j].value;
10350         int value_size = score_mapping[j].string_size;
10351
10352         strncpy(score_value, value, value_size);
10353         score_value[value_size] = '\0';
10354       }
10355       else
10356       {
10357         int *score_value = (int *)score_mapping[j].value;
10358
10359         *score_value = atoi(value);
10360       }
10361
10362       server_scores.num_entries = i + 1;
10363     }
10364
10365     server_scores.entry[i] = score_entry;
10366   }
10367
10368   freeSetupFileHash(score_hash);
10369 }
10370
10371 void LoadServerScore(int nr, boolean download_score)
10372 {
10373   if (!setup.use_api_server)
10374     return;
10375
10376   // always start with reliable default values
10377   setServerScoreInfoToDefaults();
10378
10379   // 1st step: load server scores from cache file (which may not exist)
10380   // (this should prevent reading it while the thread is writing to it)
10381   LoadServerScoreFromCache(nr);
10382
10383   if (download_score && runtime.use_api_server)
10384   {
10385     // 2nd step: download server scores from score server to cache file
10386     // (as thread, as it might time out if the server is not reachable)
10387     ApiGetScoreAsThread(nr);
10388   }
10389 }
10390
10391 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10392 {
10393   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10394
10395   // if score tape not uploaded, ask for uploading missing tapes later
10396   if (!setup.has_remaining_tapes)
10397     setup.ask_for_remaining_tapes = TRUE;
10398
10399   setup.provide_uploading_tapes = TRUE;
10400   setup.has_remaining_tapes = TRUE;
10401
10402   SaveSetup_ServerSetup();
10403 }
10404
10405 void SaveServerScore(int nr, boolean tape_saved)
10406 {
10407   if (!runtime.use_api_server)
10408   {
10409     PrepareScoreTapesForUpload(leveldir_current->subdir);
10410
10411     return;
10412   }
10413
10414   ApiAddScoreAsThread(nr, tape_saved, NULL);
10415 }
10416
10417 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10418                              char *score_tape_filename)
10419 {
10420   if (!runtime.use_api_server)
10421     return;
10422
10423   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10424 }
10425
10426 void LoadLocalAndServerScore(int nr, boolean download_score)
10427 {
10428   int last_added_local = scores.last_added_local;
10429   boolean force_last_added = scores.force_last_added;
10430
10431   // needed if only showing server scores
10432   setScoreInfoToDefaults();
10433
10434   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10435     LoadScore(nr);
10436
10437   // restore last added local score entry (before merging server scores)
10438   scores.last_added = scores.last_added_local = last_added_local;
10439
10440   if (setup.use_api_server &&
10441       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10442   {
10443     // load server scores from cache file and trigger update from server
10444     LoadServerScore(nr, download_score);
10445
10446     // merge local scores with scores from server
10447     MergeServerScore();
10448   }
10449
10450   if (force_last_added)
10451     scores.force_last_added = force_last_added;
10452 }
10453
10454
10455 // ============================================================================
10456 // setup file functions
10457 // ============================================================================
10458
10459 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10460
10461
10462 static struct TokenInfo global_setup_tokens[] =
10463 {
10464   {
10465     TYPE_STRING,
10466     &setup.player_name,                         "player_name"
10467   },
10468   {
10469     TYPE_SWITCH,
10470     &setup.multiple_users,                      "multiple_users"
10471   },
10472   {
10473     TYPE_SWITCH,
10474     &setup.sound,                               "sound"
10475   },
10476   {
10477     TYPE_SWITCH,
10478     &setup.sound_loops,                         "repeating_sound_loops"
10479   },
10480   {
10481     TYPE_SWITCH,
10482     &setup.sound_music,                         "background_music"
10483   },
10484   {
10485     TYPE_SWITCH,
10486     &setup.sound_simple,                        "simple_sound_effects"
10487   },
10488   {
10489     TYPE_SWITCH,
10490     &setup.toons,                               "toons"
10491   },
10492   {
10493     TYPE_SWITCH,
10494     &setup.global_animations,                   "global_animations"
10495   },
10496   {
10497     TYPE_SWITCH,
10498     &setup.scroll_delay,                        "scroll_delay"
10499   },
10500   {
10501     TYPE_SWITCH,
10502     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10503   },
10504   {
10505     TYPE_INTEGER,
10506     &setup.scroll_delay_value,                  "scroll_delay_value"
10507   },
10508   {
10509     TYPE_STRING,
10510     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10511   },
10512   {
10513     TYPE_INTEGER,
10514     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10515   },
10516   {
10517     TYPE_SWITCH,
10518     &setup.fade_screens,                        "fade_screens"
10519   },
10520   {
10521     TYPE_SWITCH,
10522     &setup.autorecord,                          "automatic_tape_recording"
10523   },
10524   {
10525     TYPE_SWITCH,
10526     &setup.autorecord_after_replay,             "autorecord_after_replay"
10527   },
10528   {
10529     TYPE_SWITCH,
10530     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10531   },
10532   {
10533     TYPE_SWITCH,
10534     &setup.show_titlescreen,                    "show_titlescreen"
10535   },
10536   {
10537     TYPE_SWITCH,
10538     &setup.quick_doors,                         "quick_doors"
10539   },
10540   {
10541     TYPE_SWITCH,
10542     &setup.team_mode,                           "team_mode"
10543   },
10544   {
10545     TYPE_SWITCH,
10546     &setup.handicap,                            "handicap"
10547   },
10548   {
10549     TYPE_SWITCH,
10550     &setup.skip_levels,                         "skip_levels"
10551   },
10552   {
10553     TYPE_SWITCH,
10554     &setup.increment_levels,                    "increment_levels"
10555   },
10556   {
10557     TYPE_SWITCH,
10558     &setup.auto_play_next_level,                "auto_play_next_level"
10559   },
10560   {
10561     TYPE_SWITCH,
10562     &setup.count_score_after_game,              "count_score_after_game"
10563   },
10564   {
10565     TYPE_SWITCH,
10566     &setup.show_scores_after_game,              "show_scores_after_game"
10567   },
10568   {
10569     TYPE_SWITCH,
10570     &setup.time_limit,                          "time_limit"
10571   },
10572   {
10573     TYPE_SWITCH,
10574     &setup.fullscreen,                          "fullscreen"
10575   },
10576   {
10577     TYPE_INTEGER,
10578     &setup.window_scaling_percent,              "window_scaling_percent"
10579   },
10580   {
10581     TYPE_STRING,
10582     &setup.window_scaling_quality,              "window_scaling_quality"
10583   },
10584   {
10585     TYPE_STRING,
10586     &setup.screen_rendering_mode,               "screen_rendering_mode"
10587   },
10588   {
10589     TYPE_STRING,
10590     &setup.vsync_mode,                          "vsync_mode"
10591   },
10592   {
10593     TYPE_SWITCH,
10594     &setup.ask_on_escape,                       "ask_on_escape"
10595   },
10596   {
10597     TYPE_SWITCH,
10598     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10599   },
10600   {
10601     TYPE_SWITCH,
10602     &setup.ask_on_game_over,                    "ask_on_game_over"
10603   },
10604   {
10605     TYPE_SWITCH,
10606     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10607   },
10608   {
10609     TYPE_SWITCH,
10610     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10611   },
10612   {
10613     TYPE_SWITCH,
10614     &setup.quick_switch,                        "quick_player_switch"
10615   },
10616   {
10617     TYPE_SWITCH,
10618     &setup.input_on_focus,                      "input_on_focus"
10619   },
10620   {
10621     TYPE_SWITCH,
10622     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10623   },
10624   {
10625     TYPE_SWITCH,
10626     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10627   },
10628   {
10629     TYPE_SWITCH,
10630     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10631   },
10632   {
10633     TYPE_SWITCH,
10634     &setup.game_speed_extended,                 "game_speed_extended"
10635   },
10636   {
10637     TYPE_INTEGER,
10638     &setup.game_frame_delay,                    "game_frame_delay"
10639   },
10640   {
10641     TYPE_SWITCH,
10642     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10643   },
10644   {
10645     TYPE_SWITCH,
10646     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10647   },
10648   {
10649     TYPE_SWITCH,
10650     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10651   },
10652   {
10653     TYPE_SWITCH3,
10654     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10655   },
10656   {
10657     TYPE_SWITCH,
10658     &setup.sp_show_border_elements,             "sp_show_border_elements"
10659   },
10660   {
10661     TYPE_SWITCH,
10662     &setup.small_game_graphics,                 "small_game_graphics"
10663   },
10664   {
10665     TYPE_SWITCH,
10666     &setup.show_load_save_buttons,              "show_load_save_buttons"
10667   },
10668   {
10669     TYPE_SWITCH,
10670     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10671   },
10672   {
10673     TYPE_STRING,
10674     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10675   },
10676   {
10677     TYPE_STRING,
10678     &setup.graphics_set,                        "graphics_set"
10679   },
10680   {
10681     TYPE_STRING,
10682     &setup.sounds_set,                          "sounds_set"
10683   },
10684   {
10685     TYPE_STRING,
10686     &setup.music_set,                           "music_set"
10687   },
10688   {
10689     TYPE_SWITCH3,
10690     &setup.override_level_graphics,             "override_level_graphics"
10691   },
10692   {
10693     TYPE_SWITCH3,
10694     &setup.override_level_sounds,               "override_level_sounds"
10695   },
10696   {
10697     TYPE_SWITCH3,
10698     &setup.override_level_music,                "override_level_music"
10699   },
10700   {
10701     TYPE_INTEGER,
10702     &setup.volume_simple,                       "volume_simple"
10703   },
10704   {
10705     TYPE_INTEGER,
10706     &setup.volume_loops,                        "volume_loops"
10707   },
10708   {
10709     TYPE_INTEGER,
10710     &setup.volume_music,                        "volume_music"
10711   },
10712   {
10713     TYPE_SWITCH,
10714     &setup.network_mode,                        "network_mode"
10715   },
10716   {
10717     TYPE_PLAYER,
10718     &setup.network_player_nr,                   "network_player"
10719   },
10720   {
10721     TYPE_STRING,
10722     &setup.network_server_hostname,             "network_server_hostname"
10723   },
10724   {
10725     TYPE_STRING,
10726     &setup.touch.control_type,                  "touch.control_type"
10727   },
10728   {
10729     TYPE_INTEGER,
10730     &setup.touch.move_distance,                 "touch.move_distance"
10731   },
10732   {
10733     TYPE_INTEGER,
10734     &setup.touch.drop_distance,                 "touch.drop_distance"
10735   },
10736   {
10737     TYPE_INTEGER,
10738     &setup.touch.transparency,                  "touch.transparency"
10739   },
10740   {
10741     TYPE_INTEGER,
10742     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10743   },
10744   {
10745     TYPE_INTEGER,
10746     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10747   },
10748   {
10749     TYPE_INTEGER,
10750     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10751   },
10752   {
10753     TYPE_INTEGER,
10754     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10755   },
10756   {
10757     TYPE_INTEGER,
10758     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10759   },
10760   {
10761     TYPE_INTEGER,
10762     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10763   },
10764   {
10765     TYPE_SWITCH,
10766     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10767   },
10768 };
10769
10770 static struct TokenInfo auto_setup_tokens[] =
10771 {
10772   {
10773     TYPE_INTEGER,
10774     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10775   },
10776 };
10777
10778 static struct TokenInfo server_setup_tokens[] =
10779 {
10780   {
10781     TYPE_STRING,
10782     &setup.player_uuid,                         "player_uuid"
10783   },
10784   {
10785     TYPE_INTEGER,
10786     &setup.player_version,                      "player_version"
10787   },
10788   {
10789     TYPE_SWITCH,
10790     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10791   },
10792   {
10793     TYPE_STRING,
10794     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10795   },
10796   {
10797     TYPE_STRING,
10798     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10799   },
10800   {
10801     TYPE_SWITCH,
10802     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10803   },
10804   {
10805     TYPE_SWITCH,
10806     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10807   },
10808   {
10809     TYPE_SWITCH,
10810     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10811   },
10812   {
10813     TYPE_SWITCH,
10814     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10815   },
10816   {
10817     TYPE_SWITCH,
10818     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10819   },
10820 };
10821
10822 static struct TokenInfo editor_setup_tokens[] =
10823 {
10824   {
10825     TYPE_SWITCH,
10826     &setup.editor.el_classic,                   "editor.el_classic"
10827   },
10828   {
10829     TYPE_SWITCH,
10830     &setup.editor.el_custom,                    "editor.el_custom"
10831   },
10832   {
10833     TYPE_SWITCH,
10834     &setup.editor.el_user_defined,              "editor.el_user_defined"
10835   },
10836   {
10837     TYPE_SWITCH,
10838     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10839   },
10840   {
10841     TYPE_SWITCH,
10842     &setup.editor.el_headlines,                 "editor.el_headlines"
10843   },
10844   {
10845     TYPE_SWITCH,
10846     &setup.editor.show_element_token,           "editor.show_element_token"
10847   },
10848   {
10849     TYPE_SWITCH,
10850     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10851   },
10852 };
10853
10854 static struct TokenInfo editor_cascade_setup_tokens[] =
10855 {
10856   {
10857     TYPE_SWITCH,
10858     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10859   },
10860   {
10861     TYPE_SWITCH,
10862     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10863   },
10864   {
10865     TYPE_SWITCH,
10866     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10867   },
10868   {
10869     TYPE_SWITCH,
10870     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10871   },
10872   {
10873     TYPE_SWITCH,
10874     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10875   },
10876   {
10877     TYPE_SWITCH,
10878     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10879   },
10880   {
10881     TYPE_SWITCH,
10882     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10883   },
10884   {
10885     TYPE_SWITCH,
10886     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10887   },
10888   {
10889     TYPE_SWITCH,
10890     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10891   },
10892   {
10893     TYPE_SWITCH,
10894     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10895   },
10896   {
10897     TYPE_SWITCH,
10898     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10899   },
10900   {
10901     TYPE_SWITCH,
10902     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10903   },
10904   {
10905     TYPE_SWITCH,
10906     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10907   },
10908   {
10909     TYPE_SWITCH,
10910     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10911   },
10912   {
10913     TYPE_SWITCH,
10914     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10915   },
10916   {
10917     TYPE_SWITCH,
10918     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10919   },
10920   {
10921     TYPE_SWITCH,
10922     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10923   },
10924   {
10925     TYPE_SWITCH,
10926     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10927   },
10928   {
10929     TYPE_SWITCH,
10930     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10931   },
10932   {
10933     TYPE_SWITCH,
10934     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10935   },
10936 };
10937
10938 static struct TokenInfo shortcut_setup_tokens[] =
10939 {
10940   {
10941     TYPE_KEY_X11,
10942     &setup.shortcut.save_game,                  "shortcut.save_game"
10943   },
10944   {
10945     TYPE_KEY_X11,
10946     &setup.shortcut.load_game,                  "shortcut.load_game"
10947   },
10948   {
10949     TYPE_KEY_X11,
10950     &setup.shortcut.restart_game,               "shortcut.restart_game"
10951   },
10952   {
10953     TYPE_KEY_X11,
10954     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10955   },
10956   {
10957     TYPE_KEY_X11,
10958     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10959   },
10960   {
10961     TYPE_KEY_X11,
10962     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10963   },
10964   {
10965     TYPE_KEY_X11,
10966     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10967   },
10968   {
10969     TYPE_KEY_X11,
10970     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10971   },
10972   {
10973     TYPE_KEY_X11,
10974     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10975   },
10976   {
10977     TYPE_KEY_X11,
10978     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10979   },
10980   {
10981     TYPE_KEY_X11,
10982     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10983   },
10984   {
10985     TYPE_KEY_X11,
10986     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10987   },
10988   {
10989     TYPE_KEY_X11,
10990     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10991   },
10992   {
10993     TYPE_KEY_X11,
10994     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10995   },
10996   {
10997     TYPE_KEY_X11,
10998     &setup.shortcut.tape_record,                "shortcut.tape_record"
10999   },
11000   {
11001     TYPE_KEY_X11,
11002     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11003   },
11004   {
11005     TYPE_KEY_X11,
11006     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11007   },
11008   {
11009     TYPE_KEY_X11,
11010     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11011   },
11012   {
11013     TYPE_KEY_X11,
11014     &setup.shortcut.sound_music,                "shortcut.sound_music"
11015   },
11016   {
11017     TYPE_KEY_X11,
11018     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11019   },
11020   {
11021     TYPE_KEY_X11,
11022     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11023   },
11024   {
11025     TYPE_KEY_X11,
11026     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11027   },
11028   {
11029     TYPE_KEY_X11,
11030     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11031   },
11032 };
11033
11034 static struct SetupInputInfo setup_input;
11035 static struct TokenInfo player_setup_tokens[] =
11036 {
11037   {
11038     TYPE_BOOLEAN,
11039     &setup_input.use_joystick,                  ".use_joystick"
11040   },
11041   {
11042     TYPE_STRING,
11043     &setup_input.joy.device_name,               ".joy.device_name"
11044   },
11045   {
11046     TYPE_INTEGER,
11047     &setup_input.joy.xleft,                     ".joy.xleft"
11048   },
11049   {
11050     TYPE_INTEGER,
11051     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11052   },
11053   {
11054     TYPE_INTEGER,
11055     &setup_input.joy.xright,                    ".joy.xright"
11056   },
11057   {
11058     TYPE_INTEGER,
11059     &setup_input.joy.yupper,                    ".joy.yupper"
11060   },
11061   {
11062     TYPE_INTEGER,
11063     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11064   },
11065   {
11066     TYPE_INTEGER,
11067     &setup_input.joy.ylower,                    ".joy.ylower"
11068   },
11069   {
11070     TYPE_INTEGER,
11071     &setup_input.joy.snap,                      ".joy.snap_field"
11072   },
11073   {
11074     TYPE_INTEGER,
11075     &setup_input.joy.drop,                      ".joy.place_bomb"
11076   },
11077   {
11078     TYPE_KEY_X11,
11079     &setup_input.key.left,                      ".key.move_left"
11080   },
11081   {
11082     TYPE_KEY_X11,
11083     &setup_input.key.right,                     ".key.move_right"
11084   },
11085   {
11086     TYPE_KEY_X11,
11087     &setup_input.key.up,                        ".key.move_up"
11088   },
11089   {
11090     TYPE_KEY_X11,
11091     &setup_input.key.down,                      ".key.move_down"
11092   },
11093   {
11094     TYPE_KEY_X11,
11095     &setup_input.key.snap,                      ".key.snap_field"
11096   },
11097   {
11098     TYPE_KEY_X11,
11099     &setup_input.key.drop,                      ".key.place_bomb"
11100   },
11101 };
11102
11103 static struct TokenInfo system_setup_tokens[] =
11104 {
11105   {
11106     TYPE_STRING,
11107     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11108   },
11109   {
11110     TYPE_STRING,
11111     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11112   },
11113   {
11114     TYPE_STRING,
11115     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11116   },
11117   {
11118     TYPE_INTEGER,
11119     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11120   },
11121 };
11122
11123 static struct TokenInfo internal_setup_tokens[] =
11124 {
11125   {
11126     TYPE_STRING,
11127     &setup.internal.program_title,              "program_title"
11128   },
11129   {
11130     TYPE_STRING,
11131     &setup.internal.program_version,            "program_version"
11132   },
11133   {
11134     TYPE_STRING,
11135     &setup.internal.program_author,             "program_author"
11136   },
11137   {
11138     TYPE_STRING,
11139     &setup.internal.program_email,              "program_email"
11140   },
11141   {
11142     TYPE_STRING,
11143     &setup.internal.program_website,            "program_website"
11144   },
11145   {
11146     TYPE_STRING,
11147     &setup.internal.program_copyright,          "program_copyright"
11148   },
11149   {
11150     TYPE_STRING,
11151     &setup.internal.program_company,            "program_company"
11152   },
11153   {
11154     TYPE_STRING,
11155     &setup.internal.program_icon_file,          "program_icon_file"
11156   },
11157   {
11158     TYPE_STRING,
11159     &setup.internal.default_graphics_set,       "default_graphics_set"
11160   },
11161   {
11162     TYPE_STRING,
11163     &setup.internal.default_sounds_set,         "default_sounds_set"
11164   },
11165   {
11166     TYPE_STRING,
11167     &setup.internal.default_music_set,          "default_music_set"
11168   },
11169   {
11170     TYPE_STRING,
11171     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11172   },
11173   {
11174     TYPE_STRING,
11175     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11176   },
11177   {
11178     TYPE_STRING,
11179     &setup.internal.fallback_music_file,        "fallback_music_file"
11180   },
11181   {
11182     TYPE_STRING,
11183     &setup.internal.default_level_series,       "default_level_series"
11184   },
11185   {
11186     TYPE_INTEGER,
11187     &setup.internal.default_window_width,       "default_window_width"
11188   },
11189   {
11190     TYPE_INTEGER,
11191     &setup.internal.default_window_height,      "default_window_height"
11192   },
11193   {
11194     TYPE_BOOLEAN,
11195     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11196   },
11197   {
11198     TYPE_BOOLEAN,
11199     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11200   },
11201   {
11202     TYPE_BOOLEAN,
11203     &setup.internal.create_user_levelset,       "create_user_levelset"
11204   },
11205   {
11206     TYPE_BOOLEAN,
11207     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11208   },
11209   {
11210     TYPE_BOOLEAN,
11211     &setup.internal.menu_game,                  "menu_game"
11212   },
11213   {
11214     TYPE_BOOLEAN,
11215     &setup.internal.menu_engines,               "menu_engines"
11216   },
11217   {
11218     TYPE_BOOLEAN,
11219     &setup.internal.menu_editor,                "menu_editor"
11220   },
11221   {
11222     TYPE_BOOLEAN,
11223     &setup.internal.menu_graphics,              "menu_graphics"
11224   },
11225   {
11226     TYPE_BOOLEAN,
11227     &setup.internal.menu_sound,                 "menu_sound"
11228   },
11229   {
11230     TYPE_BOOLEAN,
11231     &setup.internal.menu_artwork,               "menu_artwork"
11232   },
11233   {
11234     TYPE_BOOLEAN,
11235     &setup.internal.menu_input,                 "menu_input"
11236   },
11237   {
11238     TYPE_BOOLEAN,
11239     &setup.internal.menu_touch,                 "menu_touch"
11240   },
11241   {
11242     TYPE_BOOLEAN,
11243     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11244   },
11245   {
11246     TYPE_BOOLEAN,
11247     &setup.internal.menu_exit,                  "menu_exit"
11248   },
11249   {
11250     TYPE_BOOLEAN,
11251     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11252   },
11253   {
11254     TYPE_BOOLEAN,
11255     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11256   },
11257   {
11258     TYPE_BOOLEAN,
11259     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11260   },
11261   {
11262     TYPE_BOOLEAN,
11263     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11264   },
11265   {
11266     TYPE_BOOLEAN,
11267     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11268   },
11269   {
11270     TYPE_BOOLEAN,
11271     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11272   },
11273   {
11274     TYPE_BOOLEAN,
11275     &setup.internal.info_title,                 "info_title"
11276   },
11277   {
11278     TYPE_BOOLEAN,
11279     &setup.internal.info_elements,              "info_elements"
11280   },
11281   {
11282     TYPE_BOOLEAN,
11283     &setup.internal.info_music,                 "info_music"
11284   },
11285   {
11286     TYPE_BOOLEAN,
11287     &setup.internal.info_credits,               "info_credits"
11288   },
11289   {
11290     TYPE_BOOLEAN,
11291     &setup.internal.info_program,               "info_program"
11292   },
11293   {
11294     TYPE_BOOLEAN,
11295     &setup.internal.info_version,               "info_version"
11296   },
11297   {
11298     TYPE_BOOLEAN,
11299     &setup.internal.info_levelset,              "info_levelset"
11300   },
11301   {
11302     TYPE_BOOLEAN,
11303     &setup.internal.info_exit,                  "info_exit"
11304   },
11305 };
11306
11307 static struct TokenInfo debug_setup_tokens[] =
11308 {
11309   {
11310     TYPE_INTEGER,
11311     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11312   },
11313   {
11314     TYPE_INTEGER,
11315     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11316   },
11317   {
11318     TYPE_INTEGER,
11319     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11320   },
11321   {
11322     TYPE_INTEGER,
11323     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11324   },
11325   {
11326     TYPE_INTEGER,
11327     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11328   },
11329   {
11330     TYPE_INTEGER,
11331     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11332   },
11333   {
11334     TYPE_INTEGER,
11335     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11336   },
11337   {
11338     TYPE_INTEGER,
11339     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11340   },
11341   {
11342     TYPE_INTEGER,
11343     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11344   },
11345   {
11346     TYPE_INTEGER,
11347     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11348   },
11349   {
11350     TYPE_KEY_X11,
11351     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11352   },
11353   {
11354     TYPE_KEY_X11,
11355     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11356   },
11357   {
11358     TYPE_KEY_X11,
11359     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11360   },
11361   {
11362     TYPE_KEY_X11,
11363     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11364   },
11365   {
11366     TYPE_KEY_X11,
11367     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11368   },
11369   {
11370     TYPE_KEY_X11,
11371     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11372   },
11373   {
11374     TYPE_KEY_X11,
11375     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11376   },
11377   {
11378     TYPE_KEY_X11,
11379     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11380   },
11381   {
11382     TYPE_KEY_X11,
11383     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11384   },
11385   {
11386     TYPE_KEY_X11,
11387     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11388   },
11389   {
11390     TYPE_BOOLEAN,
11391     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11392   {
11393     TYPE_BOOLEAN,
11394     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11395   },
11396   {
11397     TYPE_BOOLEAN,
11398     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11399   },
11400   {
11401     TYPE_SWITCH3,
11402     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11403   },
11404   {
11405     TYPE_INTEGER,
11406     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11407   },
11408 };
11409
11410 static struct TokenInfo options_setup_tokens[] =
11411 {
11412   {
11413     TYPE_BOOLEAN,
11414     &setup.options.verbose,                     "options.verbose"
11415   },
11416   {
11417     TYPE_BOOLEAN,
11418     &setup.options.debug,                       "options.debug"
11419   },
11420   {
11421     TYPE_STRING,
11422     &setup.options.debug_mode,                  "options.debug_mode"
11423   },
11424 };
11425
11426 static void setSetupInfoToDefaults(struct SetupInfo *si)
11427 {
11428   int i;
11429
11430   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11431
11432   si->multiple_users = TRUE;
11433
11434   si->sound = TRUE;
11435   si->sound_loops = TRUE;
11436   si->sound_music = TRUE;
11437   si->sound_simple = TRUE;
11438   si->toons = TRUE;
11439   si->global_animations = TRUE;
11440   si->scroll_delay = TRUE;
11441   si->forced_scroll_delay = FALSE;
11442   si->scroll_delay_value = STD_SCROLL_DELAY;
11443   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11444   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11445   si->fade_screens = TRUE;
11446   si->autorecord = TRUE;
11447   si->autorecord_after_replay = TRUE;
11448   si->auto_pause_on_start = FALSE;
11449   si->show_titlescreen = TRUE;
11450   si->quick_doors = FALSE;
11451   si->team_mode = FALSE;
11452   si->handicap = TRUE;
11453   si->skip_levels = TRUE;
11454   si->increment_levels = TRUE;
11455   si->auto_play_next_level = TRUE;
11456   si->count_score_after_game = TRUE;
11457   si->show_scores_after_game = TRUE;
11458   si->time_limit = TRUE;
11459   si->fullscreen = FALSE;
11460   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11461   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11462   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11463   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11464   si->ask_on_escape = TRUE;
11465   si->ask_on_escape_editor = TRUE;
11466   si->ask_on_game_over = TRUE;
11467   si->ask_on_quit_game = TRUE;
11468   si->ask_on_quit_program = TRUE;
11469   si->quick_switch = FALSE;
11470   si->input_on_focus = FALSE;
11471   si->prefer_aga_graphics = TRUE;
11472   si->prefer_lowpass_sounds = FALSE;
11473   si->prefer_extra_panel_items = TRUE;
11474   si->game_speed_extended = FALSE;
11475   si->game_frame_delay = GAME_FRAME_DELAY;
11476   si->bd_skip_uncovering = FALSE;
11477   si->bd_skip_hatching = FALSE;
11478   si->bd_scroll_delay = TRUE;
11479   si->bd_smooth_movements = AUTO;
11480   si->sp_show_border_elements = FALSE;
11481   si->small_game_graphics = FALSE;
11482   si->show_load_save_buttons = FALSE;
11483   si->show_undo_redo_buttons = FALSE;
11484   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11485
11486   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11487   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11488   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11489
11490   si->override_level_graphics = FALSE;
11491   si->override_level_sounds = FALSE;
11492   si->override_level_music = FALSE;
11493
11494   si->volume_simple = 100;              // percent
11495   si->volume_loops = 100;               // percent
11496   si->volume_music = 100;               // percent
11497
11498   si->network_mode = FALSE;
11499   si->network_player_nr = 0;            // first player
11500   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11501
11502   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11503   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11504   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11505   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11506   si->touch.draw_outlined = TRUE;
11507   si->touch.draw_pressed = TRUE;
11508
11509   for (i = 0; i < 2; i++)
11510   {
11511     char *default_grid_button[6][2] =
11512     {
11513       { "      ", "  ^^  " },
11514       { "      ", "  ^^  " },
11515       { "      ", "<<  >>" },
11516       { "      ", "<<  >>" },
11517       { "111222", "  vv  " },
11518       { "111222", "  vv  " }
11519     };
11520     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11521     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11522     int min_xsize = MIN(6, grid_xsize);
11523     int min_ysize = MIN(6, grid_ysize);
11524     int startx = grid_xsize - min_xsize;
11525     int starty = grid_ysize - min_ysize;
11526     int x, y;
11527
11528     // virtual buttons grid can only be set to defaults if video is initialized
11529     // (this will be repeated if virtual buttons are not loaded from setup file)
11530     if (video.initialized)
11531     {
11532       si->touch.grid_xsize[i] = grid_xsize;
11533       si->touch.grid_ysize[i] = grid_ysize;
11534     }
11535     else
11536     {
11537       si->touch.grid_xsize[i] = -1;
11538       si->touch.grid_ysize[i] = -1;
11539     }
11540
11541     for (x = 0; x < MAX_GRID_XSIZE; x++)
11542       for (y = 0; y < MAX_GRID_YSIZE; y++)
11543         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11544
11545     for (x = 0; x < min_xsize; x++)
11546       for (y = 0; y < min_ysize; y++)
11547         si->touch.grid_button[i][x][starty + y] =
11548           default_grid_button[y][0][x];
11549
11550     for (x = 0; x < min_xsize; x++)
11551       for (y = 0; y < min_ysize; y++)
11552         si->touch.grid_button[i][startx + x][starty + y] =
11553           default_grid_button[y][1][x];
11554   }
11555
11556   si->touch.grid_initialized            = video.initialized;
11557
11558   si->touch.overlay_buttons             = FALSE;
11559
11560   si->editor.el_boulderdash             = TRUE;
11561   si->editor.el_boulderdash_native      = TRUE;
11562   si->editor.el_boulderdash_effects     = TRUE;
11563   si->editor.el_emerald_mine            = TRUE;
11564   si->editor.el_emerald_mine_club       = TRUE;
11565   si->editor.el_more                    = TRUE;
11566   si->editor.el_sokoban                 = TRUE;
11567   si->editor.el_supaplex                = TRUE;
11568   si->editor.el_diamond_caves           = TRUE;
11569   si->editor.el_dx_boulderdash          = TRUE;
11570
11571   si->editor.el_mirror_magic            = TRUE;
11572   si->editor.el_deflektor               = TRUE;
11573
11574   si->editor.el_chars                   = TRUE;
11575   si->editor.el_steel_chars             = TRUE;
11576
11577   si->editor.el_classic                 = TRUE;
11578   si->editor.el_custom                  = TRUE;
11579
11580   si->editor.el_user_defined            = FALSE;
11581   si->editor.el_dynamic                 = TRUE;
11582
11583   si->editor.el_headlines               = TRUE;
11584
11585   si->editor.show_element_token         = FALSE;
11586
11587   si->editor.show_read_only_warning     = TRUE;
11588
11589   si->editor.use_template_for_new_levels = TRUE;
11590
11591   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11592   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11593   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11594   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11595   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11596
11597   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11598   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11599   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11600   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11601   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11602
11603   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11604   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11605   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11606   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11607   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11608   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11609
11610   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11611   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11612   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11613
11614   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11615   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11616   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11617   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11618
11619   for (i = 0; i < MAX_PLAYERS; i++)
11620   {
11621     si->input[i].use_joystick = FALSE;
11622     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11623     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11624     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11625     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11626     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11627     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11628     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11629     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11630     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11631     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11632     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11633     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11634     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11635     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11636     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11637   }
11638
11639   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11640   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11641   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11642   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11643
11644   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11645   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11646   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11647   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11648   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11649   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11650   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11651
11652   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11653
11654   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11655   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11656   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11657
11658   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11659   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11660   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11661
11662   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11663   si->internal.choose_from_top_leveldir = FALSE;
11664   si->internal.show_scaling_in_title = TRUE;
11665   si->internal.create_user_levelset = TRUE;
11666   si->internal.info_screens_from_main = FALSE;
11667
11668   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11669   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11670
11671   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11672   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11673   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11674   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11675   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11676   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11677   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11678   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11679   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11680   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11681
11682   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11683   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11684   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11685   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11686   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11687   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11688   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11689   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11690   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11691   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11692
11693   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11694   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11695
11696   si->debug.show_frames_per_second = FALSE;
11697
11698   si->debug.xsn_mode = AUTO;
11699   si->debug.xsn_percent = 0;
11700
11701   si->options.verbose = FALSE;
11702   si->options.debug = FALSE;
11703   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11704
11705 #if defined(PLATFORM_ANDROID)
11706   si->fullscreen = TRUE;
11707   si->touch.overlay_buttons = TRUE;
11708 #endif
11709
11710   setHideSetupEntry(&setup.debug.xsn_mode);
11711 }
11712
11713 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11714 {
11715   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11716 }
11717
11718 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11719 {
11720   si->player_uuid = NULL;       // (will be set later)
11721   si->player_version = 1;       // (will be set later)
11722
11723   si->use_api_server = TRUE;
11724   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11725   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11726   si->ask_for_uploading_tapes = TRUE;
11727   si->ask_for_remaining_tapes = FALSE;
11728   si->provide_uploading_tapes = TRUE;
11729   si->ask_for_using_api_server = TRUE;
11730   si->has_remaining_tapes = FALSE;
11731 }
11732
11733 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11734 {
11735   si->editor_cascade.el_bd              = TRUE;
11736   si->editor_cascade.el_bd_native       = TRUE;
11737   si->editor_cascade.el_bd_effects      = FALSE;
11738   si->editor_cascade.el_em              = TRUE;
11739   si->editor_cascade.el_emc             = TRUE;
11740   si->editor_cascade.el_rnd             = TRUE;
11741   si->editor_cascade.el_sb              = TRUE;
11742   si->editor_cascade.el_sp              = TRUE;
11743   si->editor_cascade.el_dc              = TRUE;
11744   si->editor_cascade.el_dx              = TRUE;
11745
11746   si->editor_cascade.el_mm              = TRUE;
11747   si->editor_cascade.el_df              = TRUE;
11748
11749   si->editor_cascade.el_chars           = FALSE;
11750   si->editor_cascade.el_steel_chars     = FALSE;
11751   si->editor_cascade.el_ce              = FALSE;
11752   si->editor_cascade.el_ge              = FALSE;
11753   si->editor_cascade.el_es              = FALSE;
11754   si->editor_cascade.el_ref             = FALSE;
11755   si->editor_cascade.el_user            = FALSE;
11756   si->editor_cascade.el_dynamic         = FALSE;
11757 }
11758
11759 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11760
11761 static char *getHideSetupToken(void *setup_value)
11762 {
11763   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11764
11765   if (setup_value != NULL)
11766     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11767
11768   return hide_setup_token;
11769 }
11770
11771 void setHideSetupEntry(void *setup_value)
11772 {
11773   char *hide_setup_token = getHideSetupToken(setup_value);
11774
11775   if (hide_setup_hash == NULL)
11776     hide_setup_hash = newSetupFileHash();
11777
11778   if (setup_value != NULL)
11779     setHashEntry(hide_setup_hash, hide_setup_token, "");
11780 }
11781
11782 void removeHideSetupEntry(void *setup_value)
11783 {
11784   char *hide_setup_token = getHideSetupToken(setup_value);
11785
11786   if (setup_value != NULL)
11787     removeHashEntry(hide_setup_hash, hide_setup_token);
11788 }
11789
11790 boolean hideSetupEntry(void *setup_value)
11791 {
11792   char *hide_setup_token = getHideSetupToken(setup_value);
11793
11794   return (setup_value != NULL &&
11795           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11796 }
11797
11798 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11799                                       struct TokenInfo *token_info,
11800                                       int token_nr, char *token_text)
11801 {
11802   char *token_hide_text = getStringCat2(token_text, ".hide");
11803   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11804
11805   // set the value of this setup option in the setup option structure
11806   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11807
11808   // check if this setup option should be hidden in the setup menu
11809   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11810     setHideSetupEntry(token_info[token_nr].value);
11811
11812   free(token_hide_text);
11813 }
11814
11815 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11816                                       struct TokenInfo *token_info,
11817                                       int token_nr)
11818 {
11819   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11820                             token_info[token_nr].text);
11821 }
11822
11823 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11824 {
11825   int i, pnr;
11826
11827   if (!setup_file_hash)
11828     return;
11829
11830   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11831     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11832
11833   setup.touch.grid_initialized = TRUE;
11834   for (i = 0; i < 2; i++)
11835   {
11836     int grid_xsize = setup.touch.grid_xsize[i];
11837     int grid_ysize = setup.touch.grid_ysize[i];
11838     int x, y;
11839
11840     // if virtual buttons are not loaded from setup file, repeat initializing
11841     // virtual buttons grid with default values later when video is initialized
11842     if (grid_xsize == -1 ||
11843         grid_ysize == -1)
11844     {
11845       setup.touch.grid_initialized = FALSE;
11846
11847       continue;
11848     }
11849
11850     for (y = 0; y < grid_ysize; y++)
11851     {
11852       char token_string[MAX_LINE_LEN];
11853
11854       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11855
11856       char *value_string = getHashEntry(setup_file_hash, token_string);
11857
11858       if (value_string == NULL)
11859         continue;
11860
11861       for (x = 0; x < grid_xsize; x++)
11862       {
11863         char c = value_string[x];
11864
11865         setup.touch.grid_button[i][x][y] =
11866           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11867       }
11868     }
11869   }
11870
11871   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11872     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11873
11874   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11875     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11876
11877   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11878   {
11879     char prefix[30];
11880
11881     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11882
11883     setup_input = setup.input[pnr];
11884     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11885     {
11886       char full_token[100];
11887
11888       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11889       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11890                                 full_token);
11891     }
11892     setup.input[pnr] = setup_input;
11893   }
11894
11895   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11896     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11897
11898   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11899     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11900
11901   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11902     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11903
11904   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11905     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11906
11907   setHideRelatedSetupEntries();
11908 }
11909
11910 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11911 {
11912   int i;
11913
11914   if (!setup_file_hash)
11915     return;
11916
11917   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11918     setSetupInfo(auto_setup_tokens, i,
11919                  getHashEntry(setup_file_hash,
11920                               auto_setup_tokens[i].text));
11921 }
11922
11923 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11924 {
11925   int i;
11926
11927   if (!setup_file_hash)
11928     return;
11929
11930   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11931     setSetupInfo(server_setup_tokens, i,
11932                  getHashEntry(setup_file_hash,
11933                               server_setup_tokens[i].text));
11934 }
11935
11936 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11937 {
11938   int i;
11939
11940   if (!setup_file_hash)
11941     return;
11942
11943   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11944     setSetupInfo(editor_cascade_setup_tokens, i,
11945                  getHashEntry(setup_file_hash,
11946                               editor_cascade_setup_tokens[i].text));
11947 }
11948
11949 void LoadUserNames(void)
11950 {
11951   int last_user_nr = user.nr;
11952   int i;
11953
11954   if (global.user_names != NULL)
11955   {
11956     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11957       checked_free(global.user_names[i]);
11958
11959     checked_free(global.user_names);
11960   }
11961
11962   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11963
11964   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11965   {
11966     user.nr = i;
11967
11968     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11969
11970     if (setup_file_hash)
11971     {
11972       char *player_name = getHashEntry(setup_file_hash, "player_name");
11973
11974       global.user_names[i] = getFixedUserName(player_name);
11975
11976       freeSetupFileHash(setup_file_hash);
11977     }
11978
11979     if (global.user_names[i] == NULL)
11980       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11981   }
11982
11983   user.nr = last_user_nr;
11984 }
11985
11986 void LoadSetupFromFilename(char *filename)
11987 {
11988   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11989
11990   if (setup_file_hash)
11991   {
11992     decodeSetupFileHash_Default(setup_file_hash);
11993
11994     freeSetupFileHash(setup_file_hash);
11995   }
11996   else
11997   {
11998     Debug("setup", "using default setup values");
11999   }
12000 }
12001
12002 static void LoadSetup_SpecialPostProcessing(void)
12003 {
12004   char *player_name_new;
12005
12006   // needed to work around problems with fixed length strings
12007   player_name_new = getFixedUserName(setup.player_name);
12008   free(setup.player_name);
12009   setup.player_name = player_name_new;
12010
12011   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12012   if (setup.scroll_delay == FALSE)
12013   {
12014     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12015     setup.scroll_delay = TRUE;                  // now always "on"
12016   }
12017
12018   // make sure that scroll delay value stays inside valid range
12019   setup.scroll_delay_value =
12020     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12021 }
12022
12023 void LoadSetup_Default(void)
12024 {
12025   char *filename;
12026
12027   // always start with reliable default values
12028   setSetupInfoToDefaults(&setup);
12029
12030   // try to load setup values from default setup file
12031   filename = getDefaultSetupFilename();
12032
12033   if (fileExists(filename))
12034     LoadSetupFromFilename(filename);
12035
12036   // try to load setup values from platform setup file
12037   filename = getPlatformSetupFilename();
12038
12039   if (fileExists(filename))
12040     LoadSetupFromFilename(filename);
12041
12042   // try to load setup values from user setup file
12043   filename = getSetupFilename();
12044
12045   LoadSetupFromFilename(filename);
12046
12047   LoadSetup_SpecialPostProcessing();
12048 }
12049
12050 void LoadSetup_AutoSetup(void)
12051 {
12052   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12053   SetupFileHash *setup_file_hash = NULL;
12054
12055   // always start with reliable default values
12056   setSetupInfoToDefaults_AutoSetup(&setup);
12057
12058   setup_file_hash = loadSetupFileHash(filename);
12059
12060   if (setup_file_hash)
12061   {
12062     decodeSetupFileHash_AutoSetup(setup_file_hash);
12063
12064     freeSetupFileHash(setup_file_hash);
12065   }
12066
12067   free(filename);
12068 }
12069
12070 void LoadSetup_ServerSetup(void)
12071 {
12072   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12073   SetupFileHash *setup_file_hash = NULL;
12074
12075   // always start with reliable default values
12076   setSetupInfoToDefaults_ServerSetup(&setup);
12077
12078   setup_file_hash = loadSetupFileHash(filename);
12079
12080   if (setup_file_hash)
12081   {
12082     decodeSetupFileHash_ServerSetup(setup_file_hash);
12083
12084     freeSetupFileHash(setup_file_hash);
12085   }
12086
12087   free(filename);
12088
12089   if (setup.player_uuid == NULL)
12090   {
12091     // player UUID does not yet exist in setup file
12092     setup.player_uuid = getStringCopy(getUUID());
12093     setup.player_version = 2;
12094
12095     SaveSetup_ServerSetup();
12096   }
12097 }
12098
12099 void LoadSetup_EditorCascade(void)
12100 {
12101   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12102   SetupFileHash *setup_file_hash = NULL;
12103
12104   // always start with reliable default values
12105   setSetupInfoToDefaults_EditorCascade(&setup);
12106
12107   setup_file_hash = loadSetupFileHash(filename);
12108
12109   if (setup_file_hash)
12110   {
12111     decodeSetupFileHash_EditorCascade(setup_file_hash);
12112
12113     freeSetupFileHash(setup_file_hash);
12114   }
12115
12116   free(filename);
12117 }
12118
12119 void LoadSetup(void)
12120 {
12121   LoadSetup_Default();
12122   LoadSetup_AutoSetup();
12123   LoadSetup_ServerSetup();
12124   LoadSetup_EditorCascade();
12125 }
12126
12127 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12128                                            char *mapping_line)
12129 {
12130   char mapping_guid[MAX_LINE_LEN];
12131   char *mapping_start, *mapping_end;
12132
12133   // get GUID from game controller mapping line: copy complete line
12134   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12135   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12136
12137   // get GUID from game controller mapping line: cut after GUID part
12138   mapping_start = strchr(mapping_guid, ',');
12139   if (mapping_start != NULL)
12140     *mapping_start = '\0';
12141
12142   // cut newline from game controller mapping line
12143   mapping_end = strchr(mapping_line, '\n');
12144   if (mapping_end != NULL)
12145     *mapping_end = '\0';
12146
12147   // add mapping entry to game controller mappings hash
12148   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12149 }
12150
12151 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12152                                                  char *filename)
12153 {
12154   FILE *file;
12155
12156   if (!(file = fopen(filename, MODE_READ)))
12157   {
12158     Warn("cannot read game controller mappings file '%s'", filename);
12159
12160     return;
12161   }
12162
12163   while (!feof(file))
12164   {
12165     char line[MAX_LINE_LEN];
12166
12167     if (!fgets(line, MAX_LINE_LEN, file))
12168       break;
12169
12170     addGameControllerMappingToHash(mappings_hash, line);
12171   }
12172
12173   fclose(file);
12174 }
12175
12176 void SaveSetup_Default(void)
12177 {
12178   char *filename = getSetupFilename();
12179   FILE *file;
12180   int i, pnr;
12181
12182   InitUserDataDirectory();
12183
12184   if (!(file = fopen(filename, MODE_WRITE)))
12185   {
12186     Warn("cannot write setup file '%s'", filename);
12187
12188     return;
12189   }
12190
12191   fprintFileHeader(file, SETUP_FILENAME);
12192
12193   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12194   {
12195     // just to make things nicer :)
12196     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12197         global_setup_tokens[i].value == &setup.sound                    ||
12198         global_setup_tokens[i].value == &setup.graphics_set             ||
12199         global_setup_tokens[i].value == &setup.volume_simple            ||
12200         global_setup_tokens[i].value == &setup.network_mode             ||
12201         global_setup_tokens[i].value == &setup.touch.control_type       ||
12202         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12203         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12204       fprintf(file, "\n");
12205
12206     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12207   }
12208
12209   for (i = 0; i < 2; i++)
12210   {
12211     int grid_xsize = setup.touch.grid_xsize[i];
12212     int grid_ysize = setup.touch.grid_ysize[i];
12213     int x, y;
12214
12215     fprintf(file, "\n");
12216
12217     for (y = 0; y < grid_ysize; y++)
12218     {
12219       char token_string[MAX_LINE_LEN];
12220       char value_string[MAX_LINE_LEN];
12221
12222       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12223
12224       for (x = 0; x < grid_xsize; x++)
12225       {
12226         char c = setup.touch.grid_button[i][x][y];
12227
12228         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12229       }
12230
12231       value_string[grid_xsize] = '\0';
12232
12233       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12234     }
12235   }
12236
12237   fprintf(file, "\n");
12238   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12239     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12240
12241   fprintf(file, "\n");
12242   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12243     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12244
12245   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12246   {
12247     char prefix[30];
12248
12249     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12250     fprintf(file, "\n");
12251
12252     setup_input = setup.input[pnr];
12253     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12254       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12255   }
12256
12257   fprintf(file, "\n");
12258   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12259     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12260
12261   // (internal setup values not saved to user setup file)
12262
12263   fprintf(file, "\n");
12264   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12265     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12266         setup.debug.xsn_mode != AUTO)
12267       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12268
12269   fprintf(file, "\n");
12270   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12271     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12272
12273   fclose(file);
12274
12275   SetFilePermissions(filename, PERMS_PRIVATE);
12276 }
12277
12278 void SaveSetup_AutoSetup(void)
12279 {
12280   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12281   FILE *file;
12282   int i;
12283
12284   InitUserDataDirectory();
12285
12286   if (!(file = fopen(filename, MODE_WRITE)))
12287   {
12288     Warn("cannot write auto setup file '%s'", filename);
12289
12290     free(filename);
12291
12292     return;
12293   }
12294
12295   fprintFileHeader(file, AUTOSETUP_FILENAME);
12296
12297   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12298     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12299
12300   fclose(file);
12301
12302   SetFilePermissions(filename, PERMS_PRIVATE);
12303
12304   free(filename);
12305 }
12306
12307 void SaveSetup_ServerSetup(void)
12308 {
12309   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12310   FILE *file;
12311   int i;
12312
12313   InitUserDataDirectory();
12314
12315   if (!(file = fopen(filename, MODE_WRITE)))
12316   {
12317     Warn("cannot write server setup file '%s'", filename);
12318
12319     free(filename);
12320
12321     return;
12322   }
12323
12324   fprintFileHeader(file, SERVERSETUP_FILENAME);
12325
12326   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12327   {
12328     // just to make things nicer :)
12329     if (server_setup_tokens[i].value == &setup.use_api_server)
12330       fprintf(file, "\n");
12331
12332     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12333   }
12334
12335   fclose(file);
12336
12337   SetFilePermissions(filename, PERMS_PRIVATE);
12338
12339   free(filename);
12340 }
12341
12342 void SaveSetup_EditorCascade(void)
12343 {
12344   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12345   FILE *file;
12346   int i;
12347
12348   InitUserDataDirectory();
12349
12350   if (!(file = fopen(filename, MODE_WRITE)))
12351   {
12352     Warn("cannot write editor cascade state file '%s'", filename);
12353
12354     free(filename);
12355
12356     return;
12357   }
12358
12359   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12360
12361   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12362     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12363
12364   fclose(file);
12365
12366   SetFilePermissions(filename, PERMS_PRIVATE);
12367
12368   free(filename);
12369 }
12370
12371 void SaveSetup(void)
12372 {
12373   SaveSetup_Default();
12374   SaveSetup_AutoSetup();
12375   SaveSetup_ServerSetup();
12376   SaveSetup_EditorCascade();
12377 }
12378
12379 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12380                                                   char *filename)
12381 {
12382   FILE *file;
12383
12384   if (!(file = fopen(filename, MODE_WRITE)))
12385   {
12386     Warn("cannot write game controller mappings file '%s'", filename);
12387
12388     return;
12389   }
12390
12391   BEGIN_HASH_ITERATION(mappings_hash, itr)
12392   {
12393     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12394   }
12395   END_HASH_ITERATION(mappings_hash, itr)
12396
12397   fclose(file);
12398 }
12399
12400 void SaveSetup_AddGameControllerMapping(char *mapping)
12401 {
12402   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12403   SetupFileHash *mappings_hash = newSetupFileHash();
12404
12405   InitUserDataDirectory();
12406
12407   // load existing personal game controller mappings
12408   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12409
12410   // add new mapping to personal game controller mappings
12411   addGameControllerMappingToHash(mappings_hash, mapping);
12412
12413   // save updated personal game controller mappings
12414   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12415
12416   freeSetupFileHash(mappings_hash);
12417   free(filename);
12418 }
12419
12420 void LoadCustomElementDescriptions(void)
12421 {
12422   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12423   SetupFileHash *setup_file_hash;
12424   int i;
12425
12426   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12427   {
12428     if (element_info[i].custom_description != NULL)
12429     {
12430       free(element_info[i].custom_description);
12431       element_info[i].custom_description = NULL;
12432     }
12433   }
12434
12435   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12436     return;
12437
12438   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12439   {
12440     char *token = getStringCat2(element_info[i].token_name, ".name");
12441     char *value = getHashEntry(setup_file_hash, token);
12442
12443     if (value != NULL)
12444       element_info[i].custom_description = getStringCopy(value);
12445
12446     free(token);
12447   }
12448
12449   freeSetupFileHash(setup_file_hash);
12450 }
12451
12452 static int getElementFromToken(char *token)
12453 {
12454   char *value = getHashEntry(element_token_hash, token);
12455
12456   if (value != NULL)
12457     return atoi(value);
12458
12459   Warn("unknown element token '%s'", token);
12460
12461   return EL_UNDEFINED;
12462 }
12463
12464 void FreeGlobalAnimEventInfo(void)
12465 {
12466   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12467
12468   if (gaei->event_list == NULL)
12469     return;
12470
12471   int i;
12472
12473   for (i = 0; i < gaei->num_event_lists; i++)
12474   {
12475     checked_free(gaei->event_list[i]->event_value);
12476     checked_free(gaei->event_list[i]);
12477   }
12478
12479   checked_free(gaei->event_list);
12480
12481   gaei->event_list = NULL;
12482   gaei->num_event_lists = 0;
12483 }
12484
12485 static int AddGlobalAnimEventList(void)
12486 {
12487   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12488   int list_pos = gaei->num_event_lists++;
12489
12490   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12491                                      sizeof(struct GlobalAnimEventListInfo *));
12492
12493   gaei->event_list[list_pos] =
12494     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12495
12496   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12497
12498   gaeli->event_value = NULL;
12499   gaeli->num_event_values = 0;
12500
12501   return list_pos;
12502 }
12503
12504 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12505 {
12506   // do not add empty global animation events
12507   if (event_value == ANIM_EVENT_NONE)
12508     return list_pos;
12509
12510   // if list position is undefined, create new list
12511   if (list_pos == ANIM_EVENT_UNDEFINED)
12512     list_pos = AddGlobalAnimEventList();
12513
12514   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12515   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12516   int value_pos = gaeli->num_event_values++;
12517
12518   gaeli->event_value = checked_realloc(gaeli->event_value,
12519                                        gaeli->num_event_values * sizeof(int *));
12520
12521   gaeli->event_value[value_pos] = event_value;
12522
12523   return list_pos;
12524 }
12525
12526 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12527 {
12528   if (list_pos == ANIM_EVENT_UNDEFINED)
12529     return ANIM_EVENT_NONE;
12530
12531   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12532   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12533
12534   return gaeli->event_value[value_pos];
12535 }
12536
12537 int GetGlobalAnimEventValueCount(int list_pos)
12538 {
12539   if (list_pos == ANIM_EVENT_UNDEFINED)
12540     return 0;
12541
12542   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12543   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12544
12545   return gaeli->num_event_values;
12546 }
12547
12548 // This function checks if a string <s> of the format "string1, string2, ..."
12549 // exactly contains a string <s_contained>.
12550
12551 static boolean string_has_parameter(char *s, char *s_contained)
12552 {
12553   char *substring;
12554
12555   if (s == NULL || s_contained == NULL)
12556     return FALSE;
12557
12558   if (strlen(s_contained) > strlen(s))
12559     return FALSE;
12560
12561   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12562   {
12563     char next_char = s[strlen(s_contained)];
12564
12565     // check if next character is delimiter or whitespace
12566     if (next_char == ',' || next_char == '\0' ||
12567         next_char == ' ' || next_char == '\t')
12568       return TRUE;
12569   }
12570
12571   // check if string contains another parameter string after a comma
12572   substring = strchr(s, ',');
12573   if (substring == NULL)        // string does not contain a comma
12574     return FALSE;
12575
12576   // advance string pointer to next character after the comma
12577   substring++;
12578
12579   // skip potential whitespaces after the comma
12580   while (*substring == ' ' || *substring == '\t')
12581     substring++;
12582
12583   return string_has_parameter(substring, s_contained);
12584 }
12585
12586 static int get_anim_parameter_value_ce(char *s)
12587 {
12588   char *s_ptr = s;
12589   char *pattern_1 = "ce_change:custom_";
12590   char *pattern_2 = ".page_";
12591   int pattern_1_len = strlen(pattern_1);
12592   char *matching_char = strstr(s_ptr, pattern_1);
12593   int result = ANIM_EVENT_NONE;
12594
12595   if (matching_char == NULL)
12596     return ANIM_EVENT_NONE;
12597
12598   result = ANIM_EVENT_CE_CHANGE;
12599
12600   s_ptr = matching_char + pattern_1_len;
12601
12602   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12603   if (*s_ptr >= '0' && *s_ptr <= '9')
12604   {
12605     int gic_ce_nr = (*s_ptr++ - '0');
12606
12607     if (*s_ptr >= '0' && *s_ptr <= '9')
12608     {
12609       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12610
12611       if (*s_ptr >= '0' && *s_ptr <= '9')
12612         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12613     }
12614
12615     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12616       return ANIM_EVENT_NONE;
12617
12618     // custom element stored as 0 to 255
12619     gic_ce_nr--;
12620
12621     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12622   }
12623   else
12624   {
12625     // invalid custom element number specified
12626
12627     return ANIM_EVENT_NONE;
12628   }
12629
12630   // check for change page number ("page_X" or "page_XX") (optional)
12631   if (strPrefix(s_ptr, pattern_2))
12632   {
12633     s_ptr += strlen(pattern_2);
12634
12635     if (*s_ptr >= '0' && *s_ptr <= '9')
12636     {
12637       int gic_page_nr = (*s_ptr++ - '0');
12638
12639       if (*s_ptr >= '0' && *s_ptr <= '9')
12640         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12641
12642       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12643         return ANIM_EVENT_NONE;
12644
12645       // change page stored as 1 to 32 (0 means "all change pages")
12646
12647       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12648     }
12649     else
12650     {
12651       // invalid animation part number specified
12652
12653       return ANIM_EVENT_NONE;
12654     }
12655   }
12656
12657   // discard result if next character is neither delimiter nor whitespace
12658   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12659         *s_ptr == ' ' || *s_ptr == '\t'))
12660     return ANIM_EVENT_NONE;
12661
12662   return result;
12663 }
12664
12665 static int get_anim_parameter_value(char *s)
12666 {
12667   int event_value[] =
12668   {
12669     ANIM_EVENT_CLICK,
12670     ANIM_EVENT_INIT,
12671     ANIM_EVENT_START,
12672     ANIM_EVENT_END,
12673     ANIM_EVENT_POST
12674   };
12675   char *pattern_1[] =
12676   {
12677     "click:anim_",
12678     "init:anim_",
12679     "start:anim_",
12680     "end:anim_",
12681     "post:anim_"
12682   };
12683   char *pattern_2 = ".part_";
12684   char *matching_char = NULL;
12685   char *s_ptr = s;
12686   int pattern_1_len = 0;
12687   int result = ANIM_EVENT_NONE;
12688   int i;
12689
12690   result = get_anim_parameter_value_ce(s);
12691
12692   if (result != ANIM_EVENT_NONE)
12693     return result;
12694
12695   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12696   {
12697     matching_char = strstr(s_ptr, pattern_1[i]);
12698     pattern_1_len = strlen(pattern_1[i]);
12699     result = event_value[i];
12700
12701     if (matching_char != NULL)
12702       break;
12703   }
12704
12705   if (matching_char == NULL)
12706     return ANIM_EVENT_NONE;
12707
12708   s_ptr = matching_char + pattern_1_len;
12709
12710   // check for main animation number ("anim_X" or "anim_XX")
12711   if (*s_ptr >= '0' && *s_ptr <= '9')
12712   {
12713     int gic_anim_nr = (*s_ptr++ - '0');
12714
12715     if (*s_ptr >= '0' && *s_ptr <= '9')
12716       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12717
12718     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12719       return ANIM_EVENT_NONE;
12720
12721     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12722   }
12723   else
12724   {
12725     // invalid main animation number specified
12726
12727     return ANIM_EVENT_NONE;
12728   }
12729
12730   // check for animation part number ("part_X" or "part_XX") (optional)
12731   if (strPrefix(s_ptr, pattern_2))
12732   {
12733     s_ptr += strlen(pattern_2);
12734
12735     if (*s_ptr >= '0' && *s_ptr <= '9')
12736     {
12737       int gic_part_nr = (*s_ptr++ - '0');
12738
12739       if (*s_ptr >= '0' && *s_ptr <= '9')
12740         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12741
12742       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12743         return ANIM_EVENT_NONE;
12744
12745       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12746     }
12747     else
12748     {
12749       // invalid animation part number specified
12750
12751       return ANIM_EVENT_NONE;
12752     }
12753   }
12754
12755   // discard result if next character is neither delimiter nor whitespace
12756   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12757         *s_ptr == ' ' || *s_ptr == '\t'))
12758     return ANIM_EVENT_NONE;
12759
12760   return result;
12761 }
12762
12763 static int get_anim_parameter_values(char *s)
12764 {
12765   int list_pos = ANIM_EVENT_UNDEFINED;
12766   int event_value = ANIM_EVENT_DEFAULT;
12767
12768   if (string_has_parameter(s, "any"))
12769     event_value |= ANIM_EVENT_ANY;
12770
12771   if (string_has_parameter(s, "click:self") ||
12772       string_has_parameter(s, "click") ||
12773       string_has_parameter(s, "self"))
12774     event_value |= ANIM_EVENT_SELF;
12775
12776   if (string_has_parameter(s, "unclick:any"))
12777     event_value |= ANIM_EVENT_UNCLICK_ANY;
12778
12779   // if animation event found, add it to global animation event list
12780   if (event_value != ANIM_EVENT_NONE)
12781     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12782
12783   while (s != NULL)
12784   {
12785     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12786     event_value = get_anim_parameter_value(s);
12787
12788     // if animation event found, add it to global animation event list
12789     if (event_value != ANIM_EVENT_NONE)
12790       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12791
12792     // continue with next part of the string, starting with next comma
12793     s = strchr(s + 1, ',');
12794   }
12795
12796   return list_pos;
12797 }
12798
12799 static int get_anim_action_parameter_value(char *token)
12800 {
12801   // check most common default case first to massively speed things up
12802   if (strEqual(token, ARG_UNDEFINED))
12803     return ANIM_EVENT_ACTION_NONE;
12804
12805   int result = getImageIDFromToken(token);
12806
12807   if (result == -1)
12808   {
12809     char *gfx_token = getStringCat2("gfx.", token);
12810
12811     result = getImageIDFromToken(gfx_token);
12812
12813     checked_free(gfx_token);
12814   }
12815
12816   if (result == -1)
12817   {
12818     Key key = getKeyFromX11KeyName(token);
12819
12820     if (key != KSYM_UNDEFINED)
12821       result = -(int)key;
12822   }
12823
12824   if (result == -1)
12825   {
12826     if (isURL(token))
12827     {
12828       result = get_hash_from_string(token);     // unsigned int => int
12829       result = ABS(result);                     // may be negative now
12830       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12831
12832       setHashEntry(anim_url_hash, int2str(result, 0), token);
12833     }
12834   }
12835
12836   if (result == -1)
12837     result = ANIM_EVENT_ACTION_NONE;
12838
12839   return result;
12840 }
12841
12842 int get_parameter_value(char *value_raw, char *suffix, int type)
12843 {
12844   char *value = getStringToLower(value_raw);
12845   int result = 0;       // probably a save default value
12846
12847   if (strEqual(suffix, ".direction"))
12848   {
12849     result = (strEqual(value, "left")  ? MV_LEFT :
12850               strEqual(value, "right") ? MV_RIGHT :
12851               strEqual(value, "up")    ? MV_UP :
12852               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12853   }
12854   else if (strEqual(suffix, ".position"))
12855   {
12856     result = (strEqual(value, "left")   ? POS_LEFT :
12857               strEqual(value, "right")  ? POS_RIGHT :
12858               strEqual(value, "top")    ? POS_TOP :
12859               strEqual(value, "upper")  ? POS_UPPER :
12860               strEqual(value, "middle") ? POS_MIDDLE :
12861               strEqual(value, "lower")  ? POS_LOWER :
12862               strEqual(value, "bottom") ? POS_BOTTOM :
12863               strEqual(value, "any")    ? POS_ANY :
12864               strEqual(value, "ce")     ? POS_CE :
12865               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12866               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12867   }
12868   else if (strEqual(suffix, ".align"))
12869   {
12870     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12871               strEqual(value, "right")  ? ALIGN_RIGHT :
12872               strEqual(value, "center") ? ALIGN_CENTER :
12873               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12874   }
12875   else if (strEqual(suffix, ".valign"))
12876   {
12877     result = (strEqual(value, "top")    ? VALIGN_TOP :
12878               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12879               strEqual(value, "middle") ? VALIGN_MIDDLE :
12880               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12881   }
12882   else if (strEqual(suffix, ".anim_mode"))
12883   {
12884     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12885               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12886               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12887               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12888               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12889               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12890               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12891               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12892               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12893               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12894               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12895               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12896               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12897               string_has_parameter(value, "all")        ? ANIM_ALL :
12898               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12899               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12900               ANIM_DEFAULT);
12901
12902     if (string_has_parameter(value, "once"))
12903       result |= ANIM_ONCE;
12904
12905     if (string_has_parameter(value, "reverse"))
12906       result |= ANIM_REVERSE;
12907
12908     if (string_has_parameter(value, "opaque_player"))
12909       result |= ANIM_OPAQUE_PLAYER;
12910
12911     if (string_has_parameter(value, "static_panel"))
12912       result |= ANIM_STATIC_PANEL;
12913   }
12914   else if (strEqual(suffix, ".init_event") ||
12915            strEqual(suffix, ".anim_event"))
12916   {
12917     result = get_anim_parameter_values(value);
12918   }
12919   else if (strEqual(suffix, ".init_delay_action") ||
12920            strEqual(suffix, ".anim_delay_action") ||
12921            strEqual(suffix, ".post_delay_action") ||
12922            strEqual(suffix, ".init_event_action") ||
12923            strEqual(suffix, ".anim_event_action"))
12924   {
12925     result = get_anim_action_parameter_value(value_raw);
12926   }
12927   else if (strEqual(suffix, ".class"))
12928   {
12929     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12930               get_hash_from_string(value));
12931   }
12932   else if (strEqual(suffix, ".style"))
12933   {
12934     result = STYLE_DEFAULT;
12935
12936     if (string_has_parameter(value, "accurate_borders"))
12937       result |= STYLE_ACCURATE_BORDERS;
12938
12939     if (string_has_parameter(value, "inner_corners"))
12940       result |= STYLE_INNER_CORNERS;
12941
12942     if (string_has_parameter(value, "reverse"))
12943       result |= STYLE_REVERSE;
12944
12945     if (string_has_parameter(value, "leftmost_position"))
12946       result |= STYLE_LEFTMOST_POSITION;
12947
12948     if (string_has_parameter(value, "block_clicks"))
12949       result |= STYLE_BLOCK;
12950
12951     if (string_has_parameter(value, "passthrough_clicks"))
12952       result |= STYLE_PASSTHROUGH;
12953
12954     if (string_has_parameter(value, "multiple_actions"))
12955       result |= STYLE_MULTIPLE_ACTIONS;
12956
12957     if (string_has_parameter(value, "consume_ce_event"))
12958       result |= STYLE_CONSUME_CE_EVENT;
12959   }
12960   else if (strEqual(suffix, ".fade_mode"))
12961   {
12962     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12963               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12964               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12965               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12966               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12967               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12968               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12969               FADE_MODE_DEFAULT);
12970   }
12971   else if (strEqual(suffix, ".auto_delay_unit"))
12972   {
12973     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12974               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12975               AUTO_DELAY_UNIT_DEFAULT);
12976   }
12977   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12978   {
12979     result = gfx.get_font_from_token_function(value);
12980   }
12981   else          // generic parameter of type integer or boolean
12982   {
12983     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12984               type == TYPE_INTEGER ? get_integer_from_string(value) :
12985               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12986               ARG_UNDEFINED_VALUE);
12987   }
12988
12989   free(value);
12990
12991   return result;
12992 }
12993
12994 static int get_token_parameter_value(char *token, char *value_raw)
12995 {
12996   char *suffix;
12997
12998   if (token == NULL || value_raw == NULL)
12999     return ARG_UNDEFINED_VALUE;
13000
13001   suffix = strrchr(token, '.');
13002   if (suffix == NULL)
13003     suffix = token;
13004
13005   if (strEqual(suffix, ".element"))
13006     return getElementFromToken(value_raw);
13007
13008   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13009   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13010 }
13011
13012 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13013                                      boolean ignore_defaults)
13014 {
13015   int i;
13016
13017   for (i = 0; image_config_vars[i].token != NULL; i++)
13018   {
13019     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13020
13021     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13022     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13023       continue;
13024
13025     if (value != NULL)
13026       *image_config_vars[i].value =
13027         get_token_parameter_value(image_config_vars[i].token, value);
13028   }
13029 }
13030
13031 void InitMenuDesignSettings_Static(void)
13032 {
13033   // always start with reliable default values from static default config
13034   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13035 }
13036
13037 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13038 {
13039   int i;
13040
13041   // the following initializes hierarchical values from static configuration
13042
13043   // special case: initialize "ARG_DEFAULT" values in static default config
13044   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13045   titlescreen_initial_first_default.fade_mode  =
13046     title_initial_first_default.fade_mode;
13047   titlescreen_initial_first_default.fade_delay =
13048     title_initial_first_default.fade_delay;
13049   titlescreen_initial_first_default.post_delay =
13050     title_initial_first_default.post_delay;
13051   titlescreen_initial_first_default.auto_delay =
13052     title_initial_first_default.auto_delay;
13053   titlescreen_initial_first_default.auto_delay_unit =
13054     title_initial_first_default.auto_delay_unit;
13055   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13056   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13057   titlescreen_first_default.post_delay = title_first_default.post_delay;
13058   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13059   titlescreen_first_default.auto_delay_unit =
13060     title_first_default.auto_delay_unit;
13061   titlemessage_initial_first_default.fade_mode  =
13062     title_initial_first_default.fade_mode;
13063   titlemessage_initial_first_default.fade_delay =
13064     title_initial_first_default.fade_delay;
13065   titlemessage_initial_first_default.post_delay =
13066     title_initial_first_default.post_delay;
13067   titlemessage_initial_first_default.auto_delay =
13068     title_initial_first_default.auto_delay;
13069   titlemessage_initial_first_default.auto_delay_unit =
13070     title_initial_first_default.auto_delay_unit;
13071   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13072   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13073   titlemessage_first_default.post_delay = title_first_default.post_delay;
13074   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13075   titlemessage_first_default.auto_delay_unit =
13076     title_first_default.auto_delay_unit;
13077
13078   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13079   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13080   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13081   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13082   titlescreen_initial_default.auto_delay_unit =
13083     title_initial_default.auto_delay_unit;
13084   titlescreen_default.fade_mode  = title_default.fade_mode;
13085   titlescreen_default.fade_delay = title_default.fade_delay;
13086   titlescreen_default.post_delay = title_default.post_delay;
13087   titlescreen_default.auto_delay = title_default.auto_delay;
13088   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13089   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13090   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13091   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13092   titlemessage_initial_default.auto_delay_unit =
13093     title_initial_default.auto_delay_unit;
13094   titlemessage_default.fade_mode  = title_default.fade_mode;
13095   titlemessage_default.fade_delay = title_default.fade_delay;
13096   titlemessage_default.post_delay = title_default.post_delay;
13097   titlemessage_default.auto_delay = title_default.auto_delay;
13098   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13099
13100   // special case: initialize "ARG_DEFAULT" values in static default config
13101   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13102   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13103   {
13104     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13105     titlescreen_first[i] = titlescreen_first_default;
13106     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13107     titlemessage_first[i] = titlemessage_first_default;
13108
13109     titlescreen_initial[i] = titlescreen_initial_default;
13110     titlescreen[i] = titlescreen_default;
13111     titlemessage_initial[i] = titlemessage_initial_default;
13112     titlemessage[i] = titlemessage_default;
13113   }
13114
13115   // special case: initialize "ARG_DEFAULT" values in static default config
13116   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13117   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13118   {
13119     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13120       continue;
13121
13122     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13123     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13124     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13125   }
13126
13127   // special case: initialize "ARG_DEFAULT" values in static default config
13128   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13129   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13130   {
13131     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13132     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13133     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13134
13135     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13136       continue;
13137
13138     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13139   }
13140 }
13141
13142 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13143 {
13144   static struct
13145   {
13146     struct XY *dst, *src;
13147   }
13148   game_buttons_xy[] =
13149   {
13150     { &game.button.save,        &game.button.stop       },
13151     { &game.button.pause2,      &game.button.pause      },
13152     { &game.button.load,        &game.button.play       },
13153     { &game.button.undo,        &game.button.stop       },
13154     { &game.button.redo,        &game.button.play       },
13155
13156     { NULL,                     NULL                    }
13157   };
13158   int i, j;
13159
13160   // special case: initialize later added SETUP list size from LEVELS value
13161   if (menu.list_size[GAME_MODE_SETUP] == -1)
13162     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13163
13164   // set default position for snapshot buttons to stop/pause/play buttons
13165   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13166     if ((*game_buttons_xy[i].dst).x == -1 &&
13167         (*game_buttons_xy[i].dst).y == -1)
13168       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13169
13170   // --------------------------------------------------------------------------
13171   // dynamic viewports (including playfield margins, borders and alignments)
13172   // --------------------------------------------------------------------------
13173
13174   // dynamic viewports currently only supported for landscape mode
13175   int display_width  = MAX(video.display_width, video.display_height);
13176   int display_height = MIN(video.display_width, video.display_height);
13177
13178   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13179   {
13180     struct RectWithBorder *vp_window    = &viewport.window[i];
13181     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13182     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13183     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13184     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13185     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13186     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13187     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13188
13189     // adjust window size if min/max width/height is specified
13190
13191     if (vp_window->min_width != -1)
13192     {
13193       int window_width = display_width;
13194
13195       // when using static window height, use aspect ratio of display
13196       if (vp_window->min_height == -1)
13197         window_width = vp_window->height * display_width / display_height;
13198
13199       vp_window->width = MAX(vp_window->min_width, window_width);
13200     }
13201
13202     if (vp_window->min_height != -1)
13203     {
13204       int window_height = display_height;
13205
13206       // when using static window width, use aspect ratio of display
13207       if (vp_window->min_width == -1)
13208         window_height = vp_window->width * display_height / display_width;
13209
13210       vp_window->height = MAX(vp_window->min_height, window_height);
13211     }
13212
13213     if (vp_window->max_width != -1)
13214       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13215
13216     if (vp_window->max_height != -1)
13217       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13218
13219     int playfield_width  = vp_window->width;
13220     int playfield_height = vp_window->height;
13221
13222     // adjust playfield size and position according to specified margins
13223
13224     playfield_width  -= vp_playfield->margin_left;
13225     playfield_width  -= vp_playfield->margin_right;
13226
13227     playfield_height -= vp_playfield->margin_top;
13228     playfield_height -= vp_playfield->margin_bottom;
13229
13230     // adjust playfield size if min/max width/height is specified
13231
13232     if (vp_playfield->min_width != -1)
13233       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13234
13235     if (vp_playfield->min_height != -1)
13236       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13237
13238     if (vp_playfield->max_width != -1)
13239       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13240
13241     if (vp_playfield->max_height != -1)
13242       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13243
13244     // adjust playfield position according to specified alignment
13245
13246     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13247       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13248     else if (vp_playfield->align == ALIGN_CENTER)
13249       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13250     else if (vp_playfield->align == ALIGN_RIGHT)
13251       vp_playfield->x += playfield_width - vp_playfield->width;
13252
13253     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13254       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13255     else if (vp_playfield->valign == VALIGN_MIDDLE)
13256       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13257     else if (vp_playfield->valign == VALIGN_BOTTOM)
13258       vp_playfield->y += playfield_height - vp_playfield->height;
13259
13260     vp_playfield->x += vp_playfield->margin_left;
13261     vp_playfield->y += vp_playfield->margin_top;
13262
13263     // adjust individual playfield borders if only default border is specified
13264
13265     if (vp_playfield->border_left == -1)
13266       vp_playfield->border_left = vp_playfield->border_size;
13267     if (vp_playfield->border_right == -1)
13268       vp_playfield->border_right = vp_playfield->border_size;
13269     if (vp_playfield->border_top == -1)
13270       vp_playfield->border_top = vp_playfield->border_size;
13271     if (vp_playfield->border_bottom == -1)
13272       vp_playfield->border_bottom = vp_playfield->border_size;
13273
13274     // set dynamic playfield borders if borders are specified as undefined
13275     // (but only if window size was dynamic and playfield size was static)
13276
13277     if (dynamic_window_width && !dynamic_playfield_width)
13278     {
13279       if (vp_playfield->border_left == -1)
13280       {
13281         vp_playfield->border_left = (vp_playfield->x -
13282                                      vp_playfield->margin_left);
13283         vp_playfield->x     -= vp_playfield->border_left;
13284         vp_playfield->width += vp_playfield->border_left;
13285       }
13286
13287       if (vp_playfield->border_right == -1)
13288       {
13289         vp_playfield->border_right = (vp_window->width -
13290                                       vp_playfield->x -
13291                                       vp_playfield->width -
13292                                       vp_playfield->margin_right);
13293         vp_playfield->width += vp_playfield->border_right;
13294       }
13295     }
13296
13297     if (dynamic_window_height && !dynamic_playfield_height)
13298     {
13299       if (vp_playfield->border_top == -1)
13300       {
13301         vp_playfield->border_top = (vp_playfield->y -
13302                                     vp_playfield->margin_top);
13303         vp_playfield->y      -= vp_playfield->border_top;
13304         vp_playfield->height += vp_playfield->border_top;
13305       }
13306
13307       if (vp_playfield->border_bottom == -1)
13308       {
13309         vp_playfield->border_bottom = (vp_window->height -
13310                                        vp_playfield->y -
13311                                        vp_playfield->height -
13312                                        vp_playfield->margin_bottom);
13313         vp_playfield->height += vp_playfield->border_bottom;
13314       }
13315     }
13316
13317     // adjust playfield size to be a multiple of a defined alignment tile size
13318
13319     int align_size = vp_playfield->align_size;
13320     int playfield_xtiles = vp_playfield->width  / align_size;
13321     int playfield_ytiles = vp_playfield->height / align_size;
13322     int playfield_width_corrected  = playfield_xtiles * align_size;
13323     int playfield_height_corrected = playfield_ytiles * align_size;
13324     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13325                                  i == GFX_SPECIAL_ARG_EDITOR);
13326
13327     if (is_playfield_mode &&
13328         dynamic_playfield_width &&
13329         vp_playfield->width != playfield_width_corrected)
13330     {
13331       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13332
13333       vp_playfield->width = playfield_width_corrected;
13334
13335       if (vp_playfield->align == ALIGN_LEFT)
13336       {
13337         vp_playfield->border_left += playfield_xdiff;
13338       }
13339       else if (vp_playfield->align == ALIGN_RIGHT)
13340       {
13341         vp_playfield->border_right += playfield_xdiff;
13342       }
13343       else if (vp_playfield->align == ALIGN_CENTER)
13344       {
13345         int border_left_diff  = playfield_xdiff / 2;
13346         int border_right_diff = playfield_xdiff - border_left_diff;
13347
13348         vp_playfield->border_left  += border_left_diff;
13349         vp_playfield->border_right += border_right_diff;
13350       }
13351     }
13352
13353     if (is_playfield_mode &&
13354         dynamic_playfield_height &&
13355         vp_playfield->height != playfield_height_corrected)
13356     {
13357       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13358
13359       vp_playfield->height = playfield_height_corrected;
13360
13361       if (vp_playfield->valign == VALIGN_TOP)
13362       {
13363         vp_playfield->border_top += playfield_ydiff;
13364       }
13365       else if (vp_playfield->align == VALIGN_BOTTOM)
13366       {
13367         vp_playfield->border_right += playfield_ydiff;
13368       }
13369       else if (vp_playfield->align == VALIGN_MIDDLE)
13370       {
13371         int border_top_diff    = playfield_ydiff / 2;
13372         int border_bottom_diff = playfield_ydiff - border_top_diff;
13373
13374         vp_playfield->border_top    += border_top_diff;
13375         vp_playfield->border_bottom += border_bottom_diff;
13376       }
13377     }
13378
13379     // adjust door positions according to specified alignment
13380
13381     for (j = 0; j < 2; j++)
13382     {
13383       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13384
13385       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13386         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13387       else if (vp_door->align == ALIGN_CENTER)
13388         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13389       else if (vp_door->align == ALIGN_RIGHT)
13390         vp_door->x += vp_window->width - vp_door->width;
13391
13392       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13393         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13394       else if (vp_door->valign == VALIGN_MIDDLE)
13395         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13396       else if (vp_door->valign == VALIGN_BOTTOM)
13397         vp_door->y += vp_window->height - vp_door->height;
13398     }
13399   }
13400 }
13401
13402 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13403 {
13404   static struct
13405   {
13406     struct XYTileSize *dst, *src;
13407     int graphic;
13408   }
13409   editor_buttons_xy[] =
13410   {
13411     {
13412       &editor.button.element_left,      &editor.palette.element_left,
13413       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13414     },
13415     {
13416       &editor.button.element_middle,    &editor.palette.element_middle,
13417       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13418     },
13419     {
13420       &editor.button.element_right,     &editor.palette.element_right,
13421       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13422     },
13423
13424     { NULL,                     NULL                    }
13425   };
13426   int i;
13427
13428   // set default position for element buttons to element graphics
13429   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13430   {
13431     if ((*editor_buttons_xy[i].dst).x == -1 &&
13432         (*editor_buttons_xy[i].dst).y == -1)
13433     {
13434       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13435
13436       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13437
13438       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13439     }
13440   }
13441
13442   // adjust editor palette rows and columns if specified to be dynamic
13443
13444   if (editor.palette.cols == -1)
13445   {
13446     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13447     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13448     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13449
13450     editor.palette.cols = (vp_width - sc_width) / bt_width;
13451
13452     if (editor.palette.x == -1)
13453     {
13454       int palette_width = editor.palette.cols * bt_width + sc_width;
13455
13456       editor.palette.x = (vp_width - palette_width) / 2;
13457     }
13458   }
13459
13460   if (editor.palette.rows == -1)
13461   {
13462     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13463     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13464     int tx_height = getFontHeight(FONT_TEXT_2);
13465
13466     editor.palette.rows = (vp_height - tx_height) / bt_height;
13467
13468     if (editor.palette.y == -1)
13469     {
13470       int palette_height = editor.palette.rows * bt_height + tx_height;
13471
13472       editor.palette.y = (vp_height - palette_height) / 2;
13473     }
13474   }
13475 }
13476
13477 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13478                                                       boolean initialize)
13479 {
13480   // special case: check if network and preview player positions are redefined,
13481   // to compare this later against the main menu level preview being redefined
13482   struct TokenIntPtrInfo menu_config_players[] =
13483   {
13484     { "main.network_players.x", &menu.main.network_players.redefined    },
13485     { "main.network_players.y", &menu.main.network_players.redefined    },
13486     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13487     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13488     { "preview.x",              &preview.redefined                      },
13489     { "preview.y",              &preview.redefined                      }
13490   };
13491   int i;
13492
13493   if (initialize)
13494   {
13495     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13496       *menu_config_players[i].value = FALSE;
13497   }
13498   else
13499   {
13500     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13501       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13502         *menu_config_players[i].value = TRUE;
13503   }
13504 }
13505
13506 static void InitMenuDesignSettings_PreviewPlayers(void)
13507 {
13508   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13509 }
13510
13511 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13512 {
13513   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13514 }
13515
13516 static void LoadMenuDesignSettingsFromFilename(char *filename)
13517 {
13518   static struct TitleFadingInfo tfi;
13519   static struct TitleMessageInfo tmi;
13520   static struct TokenInfo title_tokens[] =
13521   {
13522     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13523     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13524     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13525     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13526     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13527
13528     { -1,               NULL,                   NULL                    }
13529   };
13530   static struct TokenInfo titlemessage_tokens[] =
13531   {
13532     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13533     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13534     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13535     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13536     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13537     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13538     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13539     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13540     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13541     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13542     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13543     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13544     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13545     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13546     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13547     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13548     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13549     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13550
13551     { -1,               NULL,                   NULL                    }
13552   };
13553   static struct
13554   {
13555     struct TitleFadingInfo *info;
13556     char *text;
13557   }
13558   title_info[] =
13559   {
13560     // initialize first titles from "enter screen" definitions, if defined
13561     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13562     { &title_first_default,             "menu.enter_screen.TITLE"       },
13563
13564     // initialize title screens from "next screen" definitions, if defined
13565     { &title_initial_default,           "menu.next_screen.TITLE"        },
13566     { &title_default,                   "menu.next_screen.TITLE"        },
13567
13568     { NULL,                             NULL                            }
13569   };
13570   static struct
13571   {
13572     struct TitleMessageInfo *array;
13573     char *text;
13574   }
13575   titlemessage_arrays[] =
13576   {
13577     // initialize first titles from "enter screen" definitions, if defined
13578     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13579     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13580     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13581     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13582
13583     // initialize titles from "next screen" definitions, if defined
13584     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13585     { titlescreen,                      "menu.next_screen.TITLE"        },
13586     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13587     { titlemessage,                     "menu.next_screen.TITLE"        },
13588
13589     // overwrite titles with title definitions, if defined
13590     { titlescreen_initial_first,        "[title_initial]"               },
13591     { titlescreen_first,                "[title]"                       },
13592     { titlemessage_initial_first,       "[title_initial]"               },
13593     { titlemessage_first,               "[title]"                       },
13594
13595     { titlescreen_initial,              "[title_initial]"               },
13596     { titlescreen,                      "[title]"                       },
13597     { titlemessage_initial,             "[title_initial]"               },
13598     { titlemessage,                     "[title]"                       },
13599
13600     // overwrite titles with title screen/message definitions, if defined
13601     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13602     { titlescreen_first,                "[titlescreen]"                 },
13603     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13604     { titlemessage_first,               "[titlemessage]"                },
13605
13606     { titlescreen_initial,              "[titlescreen_initial]"         },
13607     { titlescreen,                      "[titlescreen]"                 },
13608     { titlemessage_initial,             "[titlemessage_initial]"        },
13609     { titlemessage,                     "[titlemessage]"                },
13610
13611     { NULL,                             NULL                            }
13612   };
13613   SetupFileHash *setup_file_hash;
13614   int i, j, k;
13615
13616   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13617     return;
13618
13619   // the following initializes hierarchical values from dynamic configuration
13620
13621   // special case: initialize with default values that may be overwritten
13622   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13623   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13624   {
13625     struct TokenIntPtrInfo menu_config[] =
13626     {
13627       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13628       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13629       { "menu.list_size",       &menu.list_size[i]      }
13630     };
13631
13632     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13633     {
13634       char *token = menu_config[j].token;
13635       char *value = getHashEntry(setup_file_hash, token);
13636
13637       if (value != NULL)
13638         *menu_config[j].value = get_integer_from_string(value);
13639     }
13640   }
13641
13642   // special case: initialize with default values that may be overwritten
13643   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13644   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13645   {
13646     struct TokenIntPtrInfo menu_config[] =
13647     {
13648       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13649       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13650       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13651       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13652       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13653     };
13654
13655     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13656     {
13657       char *token = menu_config[j].token;
13658       char *value = getHashEntry(setup_file_hash, token);
13659
13660       if (value != NULL)
13661         *menu_config[j].value = get_integer_from_string(value);
13662     }
13663   }
13664
13665   // special case: initialize with default values that may be overwritten
13666   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13667   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13668   {
13669     struct TokenIntPtrInfo menu_config[] =
13670     {
13671       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13672       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13673     };
13674
13675     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13676     {
13677       char *token = menu_config[j].token;
13678       char *value = getHashEntry(setup_file_hash, token);
13679
13680       if (value != NULL)
13681         *menu_config[j].value = get_integer_from_string(value);
13682     }
13683   }
13684
13685   // special case: initialize with default values that may be overwritten
13686   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13687   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13688   {
13689     struct TokenIntPtrInfo menu_config[] =
13690     {
13691       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13692       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13693       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13694       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13695       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13696       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13697       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13698       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13699       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13700       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13701     };
13702
13703     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13704     {
13705       char *token = menu_config[j].token;
13706       char *value = getHashEntry(setup_file_hash, token);
13707
13708       if (value != NULL)
13709         *menu_config[j].value = get_integer_from_string(value);
13710     }
13711   }
13712
13713   // special case: initialize with default values that may be overwritten
13714   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13715   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13716   {
13717     struct TokenIntPtrInfo menu_config[] =
13718     {
13719       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13720       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13721       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13722       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13723       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13724       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13725       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13726       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13727       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13728     };
13729
13730     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13731     {
13732       char *token = menu_config[j].token;
13733       char *value = getHashEntry(setup_file_hash, token);
13734
13735       if (value != NULL)
13736         *menu_config[j].value = get_token_parameter_value(token, value);
13737     }
13738   }
13739
13740   // special case: initialize with default values that may be overwritten
13741   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13742   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13743   {
13744     struct
13745     {
13746       char *token_prefix;
13747       struct RectWithBorder *struct_ptr;
13748     }
13749     vp_struct[] =
13750     {
13751       { "viewport.window",      &viewport.window[i]     },
13752       { "viewport.playfield",   &viewport.playfield[i]  },
13753       { "viewport.door_1",      &viewport.door_1[i]     },
13754       { "viewport.door_2",      &viewport.door_2[i]     }
13755     };
13756
13757     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13758     {
13759       struct TokenIntPtrInfo vp_config[] =
13760       {
13761         { ".x",                 &vp_struct[j].struct_ptr->x             },
13762         { ".y",                 &vp_struct[j].struct_ptr->y             },
13763         { ".width",             &vp_struct[j].struct_ptr->width         },
13764         { ".height",            &vp_struct[j].struct_ptr->height        },
13765         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13766         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13767         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13768         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13769         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13770         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13771         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13772         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13773         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13774         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13775         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13776         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13777         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13778         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13779         { ".align",             &vp_struct[j].struct_ptr->align         },
13780         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13781       };
13782
13783       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13784       {
13785         char *token = getStringCat2(vp_struct[j].token_prefix,
13786                                     vp_config[k].token);
13787         char *value = getHashEntry(setup_file_hash, token);
13788
13789         if (value != NULL)
13790           *vp_config[k].value = get_token_parameter_value(token, value);
13791
13792         free(token);
13793       }
13794     }
13795   }
13796
13797   // special case: initialize with default values that may be overwritten
13798   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13799   for (i = 0; title_info[i].info != NULL; i++)
13800   {
13801     struct TitleFadingInfo *info = title_info[i].info;
13802     char *base_token = title_info[i].text;
13803
13804     for (j = 0; title_tokens[j].type != -1; j++)
13805     {
13806       char *token = getStringCat2(base_token, title_tokens[j].text);
13807       char *value = getHashEntry(setup_file_hash, token);
13808
13809       if (value != NULL)
13810       {
13811         int parameter_value = get_token_parameter_value(token, value);
13812
13813         tfi = *info;
13814
13815         *(int *)title_tokens[j].value = (int)parameter_value;
13816
13817         *info = tfi;
13818       }
13819
13820       free(token);
13821     }
13822   }
13823
13824   // special case: initialize with default values that may be overwritten
13825   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13826   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13827   {
13828     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13829     char *base_token = titlemessage_arrays[i].text;
13830
13831     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13832     {
13833       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13834       char *value = getHashEntry(setup_file_hash, token);
13835
13836       if (value != NULL)
13837       {
13838         int parameter_value = get_token_parameter_value(token, value);
13839
13840         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13841         {
13842           tmi = array[k];
13843
13844           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13845             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13846           else
13847             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13848
13849           array[k] = tmi;
13850         }
13851       }
13852
13853       free(token);
13854     }
13855   }
13856
13857   // read (and overwrite with) values that may be specified in config file
13858   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13859
13860   // special case: check if network and preview player positions are redefined
13861   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13862
13863   freeSetupFileHash(setup_file_hash);
13864 }
13865
13866 void LoadMenuDesignSettings(void)
13867 {
13868   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13869
13870   InitMenuDesignSettings_Static();
13871   InitMenuDesignSettings_SpecialPreProcessing();
13872   InitMenuDesignSettings_PreviewPlayers();
13873
13874   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13875   {
13876     // first look for special settings configured in level series config
13877     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13878
13879     if (fileExists(filename_base))
13880       LoadMenuDesignSettingsFromFilename(filename_base);
13881   }
13882
13883   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13884
13885   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13886     LoadMenuDesignSettingsFromFilename(filename_local);
13887
13888   InitMenuDesignSettings_SpecialPostProcessing();
13889 }
13890
13891 void LoadMenuDesignSettings_AfterGraphics(void)
13892 {
13893   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13894 }
13895
13896 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13897                                 boolean ignore_defaults)
13898 {
13899   int i;
13900
13901   for (i = 0; sound_config_vars[i].token != NULL; i++)
13902   {
13903     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13904
13905     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13906     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13907       continue;
13908
13909     if (value != NULL)
13910       *sound_config_vars[i].value =
13911         get_token_parameter_value(sound_config_vars[i].token, value);
13912   }
13913 }
13914
13915 void InitSoundSettings_Static(void)
13916 {
13917   // always start with reliable default values from static default config
13918   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13919 }
13920
13921 static void LoadSoundSettingsFromFilename(char *filename)
13922 {
13923   SetupFileHash *setup_file_hash;
13924
13925   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13926     return;
13927
13928   // read (and overwrite with) values that may be specified in config file
13929   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13930
13931   freeSetupFileHash(setup_file_hash);
13932 }
13933
13934 void LoadSoundSettings(void)
13935 {
13936   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13937
13938   InitSoundSettings_Static();
13939
13940   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13941   {
13942     // first look for special settings configured in level series config
13943     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13944
13945     if (fileExists(filename_base))
13946       LoadSoundSettingsFromFilename(filename_base);
13947   }
13948
13949   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13950
13951   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13952     LoadSoundSettingsFromFilename(filename_local);
13953 }
13954
13955 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13956 {
13957   char *filename = getEditorSetupFilename();
13958   SetupFileList *setup_file_list, *list;
13959   SetupFileHash *element_hash;
13960   int num_unknown_tokens = 0;
13961   int i;
13962
13963   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13964     return;
13965
13966   element_hash = newSetupFileHash();
13967
13968   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13969     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13970
13971   // determined size may be larger than needed (due to unknown elements)
13972   *num_elements = 0;
13973   for (list = setup_file_list; list != NULL; list = list->next)
13974     (*num_elements)++;
13975
13976   // add space for up to 3 more elements for padding that may be needed
13977   *num_elements += 3;
13978
13979   // free memory for old list of elements, if needed
13980   checked_free(*elements);
13981
13982   // allocate memory for new list of elements
13983   *elements = checked_malloc(*num_elements * sizeof(int));
13984
13985   *num_elements = 0;
13986   for (list = setup_file_list; list != NULL; list = list->next)
13987   {
13988     char *value = getHashEntry(element_hash, list->token);
13989
13990     if (value == NULL)          // try to find obsolete token mapping
13991     {
13992       char *mapped_token = get_mapped_token(list->token);
13993
13994       if (mapped_token != NULL)
13995       {
13996         value = getHashEntry(element_hash, mapped_token);
13997
13998         free(mapped_token);
13999       }
14000     }
14001
14002     if (value != NULL)
14003     {
14004       (*elements)[(*num_elements)++] = atoi(value);
14005     }
14006     else
14007     {
14008       if (num_unknown_tokens == 0)
14009       {
14010         Warn("---");
14011         Warn("unknown token(s) found in config file:");
14012         Warn("- config file: '%s'", filename);
14013
14014         num_unknown_tokens++;
14015       }
14016
14017       Warn("- token: '%s'", list->token);
14018     }
14019   }
14020
14021   if (num_unknown_tokens > 0)
14022     Warn("---");
14023
14024   while (*num_elements % 4)     // pad with empty elements, if needed
14025     (*elements)[(*num_elements)++] = EL_EMPTY;
14026
14027   freeSetupFileList(setup_file_list);
14028   freeSetupFileHash(element_hash);
14029
14030 #if 0
14031   for (i = 0; i < *num_elements; i++)
14032     Debug("editor", "element '%s' [%d]\n",
14033           element_info[(*elements)[i]].token_name, (*elements)[i]);
14034 #endif
14035 }
14036
14037 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14038                                                      boolean is_sound)
14039 {
14040   SetupFileHash *setup_file_hash = NULL;
14041   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14042   char *filename_music, *filename_prefix, *filename_info;
14043   struct
14044   {
14045     char *token;
14046     char **value_ptr;
14047   }
14048   token_to_value_ptr[] =
14049   {
14050     { "title_header",   &tmp_music_file_info.title_header       },
14051     { "artist_header",  &tmp_music_file_info.artist_header      },
14052     { "album_header",   &tmp_music_file_info.album_header       },
14053     { "year_header",    &tmp_music_file_info.year_header        },
14054     { "played_header",  &tmp_music_file_info.played_header      },
14055
14056     { "title",          &tmp_music_file_info.title              },
14057     { "artist",         &tmp_music_file_info.artist             },
14058     { "album",          &tmp_music_file_info.album              },
14059     { "year",           &tmp_music_file_info.year               },
14060     { "played",         &tmp_music_file_info.played             },
14061
14062     { NULL,             NULL                                    },
14063   };
14064   int i;
14065
14066   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14067                     getCustomMusicFilename(basename));
14068
14069   if (filename_music == NULL)
14070     return NULL;
14071
14072   // ---------- try to replace file extension ----------
14073
14074   filename_prefix = getStringCopy(filename_music);
14075   if (strrchr(filename_prefix, '.') != NULL)
14076     *strrchr(filename_prefix, '.') = '\0';
14077   filename_info = getStringCat2(filename_prefix, ".txt");
14078
14079   if (fileExists(filename_info))
14080     setup_file_hash = loadSetupFileHash(filename_info);
14081
14082   free(filename_prefix);
14083   free(filename_info);
14084
14085   if (setup_file_hash == NULL)
14086   {
14087     // ---------- try to add file extension ----------
14088
14089     filename_prefix = getStringCopy(filename_music);
14090     filename_info = getStringCat2(filename_prefix, ".txt");
14091
14092     if (fileExists(filename_info))
14093       setup_file_hash = loadSetupFileHash(filename_info);
14094
14095     free(filename_prefix);
14096     free(filename_info);
14097   }
14098
14099   if (setup_file_hash == NULL)
14100     return NULL;
14101
14102   // ---------- music file info found ----------
14103
14104   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14105
14106   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14107   {
14108     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14109
14110     *token_to_value_ptr[i].value_ptr =
14111       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14112   }
14113
14114   tmp_music_file_info.basename = getStringCopy(basename);
14115   tmp_music_file_info.music = music;
14116   tmp_music_file_info.is_sound = is_sound;
14117
14118   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14119   *new_music_file_info = tmp_music_file_info;
14120
14121   return new_music_file_info;
14122 }
14123
14124 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14125 {
14126   return get_music_file_info_ext(basename, music, FALSE);
14127 }
14128
14129 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14130 {
14131   return get_music_file_info_ext(basename, sound, TRUE);
14132 }
14133
14134 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14135                                      char *basename, boolean is_sound)
14136 {
14137   for (; list != NULL; list = list->next)
14138     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14139       return TRUE;
14140
14141   return FALSE;
14142 }
14143
14144 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14145 {
14146   return music_info_listed_ext(list, basename, FALSE);
14147 }
14148
14149 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14150 {
14151   return music_info_listed_ext(list, basename, TRUE);
14152 }
14153
14154 void LoadMusicInfo(void)
14155 {
14156   int num_music_noconf = getMusicListSize_NoConf();
14157   int num_music = getMusicListSize();
14158   int num_sounds = getSoundListSize();
14159   struct FileInfo *music, *sound;
14160   struct MusicFileInfo *next, **new;
14161
14162   int i;
14163
14164   while (music_file_info != NULL)
14165   {
14166     next = music_file_info->next;
14167
14168     checked_free(music_file_info->basename);
14169
14170     checked_free(music_file_info->title_header);
14171     checked_free(music_file_info->artist_header);
14172     checked_free(music_file_info->album_header);
14173     checked_free(music_file_info->year_header);
14174     checked_free(music_file_info->played_header);
14175
14176     checked_free(music_file_info->title);
14177     checked_free(music_file_info->artist);
14178     checked_free(music_file_info->album);
14179     checked_free(music_file_info->year);
14180     checked_free(music_file_info->played);
14181
14182     free(music_file_info);
14183
14184     music_file_info = next;
14185   }
14186
14187   new = &music_file_info;
14188
14189   // get (configured or unconfigured) music file info for all levels
14190   for (i = leveldir_current->first_level;
14191        i <= leveldir_current->last_level; i++)
14192   {
14193     int music_nr;
14194
14195     if (levelset.music[i] != MUS_UNDEFINED)
14196     {
14197       // get music file info for configured level music
14198       music_nr = levelset.music[i];
14199     }
14200     else if (num_music_noconf > 0)
14201     {
14202       // get music file info for unconfigured level music
14203       int level_pos = i - leveldir_current->first_level;
14204
14205       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14206     }
14207     else
14208     {
14209       continue;
14210     }
14211
14212     char *basename = getMusicInfoEntryFilename(music_nr);
14213
14214     if (basename == NULL)
14215       continue;
14216
14217     if (!music_info_listed(music_file_info, basename))
14218     {
14219       *new = get_music_file_info(basename, music_nr);
14220
14221       if (*new != NULL)
14222         new = &(*new)->next;
14223     }
14224   }
14225
14226   // get music file info for all remaining configured music files
14227   for (i = 0; i < num_music; i++)
14228   {
14229     music = getMusicListEntry(i);
14230
14231     if (music->filename == NULL)
14232       continue;
14233
14234     if (strEqual(music->filename, UNDEFINED_FILENAME))
14235       continue;
14236
14237     // a configured file may be not recognized as music
14238     if (!FileIsMusic(music->filename))
14239       continue;
14240
14241     if (!music_info_listed(music_file_info, music->filename))
14242     {
14243       *new = get_music_file_info(music->filename, i);
14244
14245       if (*new != NULL)
14246         new = &(*new)->next;
14247     }
14248   }
14249
14250   // get sound file info for all configured sound files
14251   for (i = 0; i < num_sounds; i++)
14252   {
14253     sound = getSoundListEntry(i);
14254
14255     if (sound->filename == NULL)
14256       continue;
14257
14258     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14259       continue;
14260
14261     // a configured file may be not recognized as sound
14262     if (!FileIsSound(sound->filename))
14263       continue;
14264
14265     if (!sound_info_listed(music_file_info, sound->filename))
14266     {
14267       *new = get_sound_file_info(sound->filename, i);
14268       if (*new != NULL)
14269         new = &(*new)->next;
14270     }
14271   }
14272
14273   // add pointers to previous list nodes
14274
14275   struct MusicFileInfo *node = music_file_info;
14276
14277   while (node != NULL)
14278   {
14279     if (node->next)
14280       node->next->prev = node;
14281
14282     node = node->next;
14283   }
14284 }
14285
14286 static void add_helpanim_entry(int element, int action, int direction,
14287                                int delay, int *num_list_entries)
14288 {
14289   struct HelpAnimInfo *new_list_entry;
14290   (*num_list_entries)++;
14291
14292   helpanim_info =
14293     checked_realloc(helpanim_info,
14294                     *num_list_entries * sizeof(struct HelpAnimInfo));
14295   new_list_entry = &helpanim_info[*num_list_entries - 1];
14296
14297   new_list_entry->element = element;
14298   new_list_entry->action = action;
14299   new_list_entry->direction = direction;
14300   new_list_entry->delay = delay;
14301 }
14302
14303 static void print_unknown_token(char *filename, char *token, int token_nr)
14304 {
14305   if (token_nr == 0)
14306   {
14307     Warn("---");
14308     Warn("unknown token(s) found in config file:");
14309     Warn("- config file: '%s'", filename);
14310   }
14311
14312   Warn("- token: '%s'", token);
14313 }
14314
14315 static void print_unknown_token_end(int token_nr)
14316 {
14317   if (token_nr > 0)
14318     Warn("---");
14319 }
14320
14321 void LoadHelpAnimInfo(void)
14322 {
14323   char *filename = getHelpAnimFilename();
14324   SetupFileList *setup_file_list = NULL, *list;
14325   SetupFileHash *element_hash, *action_hash, *direction_hash;
14326   int num_list_entries = 0;
14327   int num_unknown_tokens = 0;
14328   int i;
14329
14330   if (fileExists(filename))
14331     setup_file_list = loadSetupFileList(filename);
14332
14333   if (setup_file_list == NULL)
14334   {
14335     // use reliable default values from static configuration
14336     SetupFileList *insert_ptr;
14337
14338     insert_ptr = setup_file_list =
14339       newSetupFileList(helpanim_config[0].token,
14340                        helpanim_config[0].value);
14341
14342     for (i = 1; helpanim_config[i].token; i++)
14343       insert_ptr = addListEntry(insert_ptr,
14344                                 helpanim_config[i].token,
14345                                 helpanim_config[i].value);
14346   }
14347
14348   element_hash   = newSetupFileHash();
14349   action_hash    = newSetupFileHash();
14350   direction_hash = newSetupFileHash();
14351
14352   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14353     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14354
14355   for (i = 0; i < NUM_ACTIONS; i++)
14356     setHashEntry(action_hash, element_action_info[i].suffix,
14357                  i_to_a(element_action_info[i].value));
14358
14359   // do not store direction index (bit) here, but direction value!
14360   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14361     setHashEntry(direction_hash, element_direction_info[i].suffix,
14362                  i_to_a(1 << element_direction_info[i].value));
14363
14364   for (list = setup_file_list; list != NULL; list = list->next)
14365   {
14366     char *element_token, *action_token, *direction_token;
14367     char *element_value, *action_value, *direction_value;
14368     int delay = atoi(list->value);
14369
14370     if (strEqual(list->token, "end"))
14371     {
14372       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14373
14374       continue;
14375     }
14376
14377     /* first try to break element into element/action/direction parts;
14378        if this does not work, also accept combined "element[.act][.dir]"
14379        elements (like "dynamite.active"), which are unique elements */
14380
14381     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14382     {
14383       element_value = getHashEntry(element_hash, list->token);
14384       if (element_value != NULL)        // element found
14385         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14386                            &num_list_entries);
14387       else
14388       {
14389         // no further suffixes found -- this is not an element
14390         print_unknown_token(filename, list->token, num_unknown_tokens++);
14391       }
14392
14393       continue;
14394     }
14395
14396     // token has format "<prefix>.<something>"
14397
14398     action_token = strchr(list->token, '.');    // suffix may be action ...
14399     direction_token = action_token;             // ... or direction
14400
14401     element_token = getStringCopy(list->token);
14402     *strchr(element_token, '.') = '\0';
14403
14404     element_value = getHashEntry(element_hash, element_token);
14405
14406     if (element_value == NULL)          // this is no element
14407     {
14408       element_value = getHashEntry(element_hash, list->token);
14409       if (element_value != NULL)        // combined element found
14410         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14411                            &num_list_entries);
14412       else
14413         print_unknown_token(filename, list->token, num_unknown_tokens++);
14414
14415       free(element_token);
14416
14417       continue;
14418     }
14419
14420     action_value = getHashEntry(action_hash, action_token);
14421
14422     if (action_value != NULL)           // action found
14423     {
14424       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14425                     &num_list_entries);
14426
14427       free(element_token);
14428
14429       continue;
14430     }
14431
14432     direction_value = getHashEntry(direction_hash, direction_token);
14433
14434     if (direction_value != NULL)        // direction found
14435     {
14436       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14437                          &num_list_entries);
14438
14439       free(element_token);
14440
14441       continue;
14442     }
14443
14444     if (strchr(action_token + 1, '.') == NULL)
14445     {
14446       // no further suffixes found -- this is not an action nor direction
14447
14448       element_value = getHashEntry(element_hash, list->token);
14449       if (element_value != NULL)        // combined element found
14450         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14451                            &num_list_entries);
14452       else
14453         print_unknown_token(filename, list->token, num_unknown_tokens++);
14454
14455       free(element_token);
14456
14457       continue;
14458     }
14459
14460     // token has format "<prefix>.<suffix>.<something>"
14461
14462     direction_token = strchr(action_token + 1, '.');
14463
14464     action_token = getStringCopy(action_token);
14465     *strchr(action_token + 1, '.') = '\0';
14466
14467     action_value = getHashEntry(action_hash, action_token);
14468
14469     if (action_value == NULL)           // this is no action
14470     {
14471       element_value = getHashEntry(element_hash, list->token);
14472       if (element_value != NULL)        // combined element found
14473         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14474                            &num_list_entries);
14475       else
14476         print_unknown_token(filename, list->token, num_unknown_tokens++);
14477
14478       free(element_token);
14479       free(action_token);
14480
14481       continue;
14482     }
14483
14484     direction_value = getHashEntry(direction_hash, direction_token);
14485
14486     if (direction_value != NULL)        // direction found
14487     {
14488       add_helpanim_entry(atoi(element_value), atoi(action_value),
14489                          atoi(direction_value), delay, &num_list_entries);
14490
14491       free(element_token);
14492       free(action_token);
14493
14494       continue;
14495     }
14496
14497     // this is no direction
14498
14499     element_value = getHashEntry(element_hash, list->token);
14500     if (element_value != NULL)          // combined element found
14501       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14502                          &num_list_entries);
14503     else
14504       print_unknown_token(filename, list->token, num_unknown_tokens++);
14505
14506     free(element_token);
14507     free(action_token);
14508   }
14509
14510   print_unknown_token_end(num_unknown_tokens);
14511
14512   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14513   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14514
14515   freeSetupFileList(setup_file_list);
14516   freeSetupFileHash(element_hash);
14517   freeSetupFileHash(action_hash);
14518   freeSetupFileHash(direction_hash);
14519
14520 #if 0
14521   for (i = 0; i < num_list_entries; i++)
14522     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14523           EL_NAME(helpanim_info[i].element),
14524           helpanim_info[i].element,
14525           helpanim_info[i].action,
14526           helpanim_info[i].direction,
14527           helpanim_info[i].delay);
14528 #endif
14529 }
14530
14531 void LoadHelpTextInfo(void)
14532 {
14533   char *filename = getHelpTextFilename();
14534   int i;
14535
14536   if (helptext_info != NULL)
14537   {
14538     freeSetupFileHash(helptext_info);
14539     helptext_info = NULL;
14540   }
14541
14542   if (fileExists(filename))
14543     helptext_info = loadSetupFileHash(filename);
14544
14545   if (helptext_info == NULL)
14546   {
14547     // use reliable default values from static configuration
14548     helptext_info = newSetupFileHash();
14549
14550     for (i = 0; helptext_config[i].token; i++)
14551       setHashEntry(helptext_info,
14552                    helptext_config[i].token,
14553                    helptext_config[i].value);
14554   }
14555
14556 #if 0
14557   BEGIN_HASH_ITERATION(helptext_info, itr)
14558   {
14559     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14560           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14561   }
14562   END_HASH_ITERATION(hash, itr)
14563 #endif
14564 }
14565
14566
14567 // ----------------------------------------------------------------------------
14568 // convert levels
14569 // ----------------------------------------------------------------------------
14570
14571 #define MAX_NUM_CONVERT_LEVELS          1000
14572
14573 void ConvertLevels(void)
14574 {
14575   static LevelDirTree *convert_leveldir = NULL;
14576   static int convert_level_nr = -1;
14577   static int num_levels_handled = 0;
14578   static int num_levels_converted = 0;
14579   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14580   int i;
14581
14582   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14583                                                global.convert_leveldir);
14584
14585   if (convert_leveldir == NULL)
14586     Fail("no such level identifier: '%s'", global.convert_leveldir);
14587
14588   leveldir_current = convert_leveldir;
14589
14590   if (global.convert_level_nr != -1)
14591   {
14592     convert_leveldir->first_level = global.convert_level_nr;
14593     convert_leveldir->last_level  = global.convert_level_nr;
14594   }
14595
14596   convert_level_nr = convert_leveldir->first_level;
14597
14598   PrintLine("=", 79);
14599   Print("Converting levels\n");
14600   PrintLine("-", 79);
14601   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14602   Print("Level series name:       '%s'\n", convert_leveldir->name);
14603   Print("Level series author:     '%s'\n", convert_leveldir->author);
14604   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14605   PrintLine("=", 79);
14606   Print("\n");
14607
14608   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14609     levels_failed[i] = FALSE;
14610
14611   while (convert_level_nr <= convert_leveldir->last_level)
14612   {
14613     char *level_filename;
14614     boolean new_level;
14615
14616     level_nr = convert_level_nr++;
14617
14618     Print("Level %03d: ", level_nr);
14619
14620     LoadLevel(level_nr);
14621     if (level.no_level_file || level.no_valid_file)
14622     {
14623       Print("(no level)\n");
14624       continue;
14625     }
14626
14627     Print("converting level ... ");
14628
14629 #if 0
14630     // special case: conversion of some EMC levels as requested by ACME
14631     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14632 #endif
14633
14634     level_filename = getDefaultLevelFilename(level_nr);
14635     new_level = !fileExists(level_filename);
14636
14637     if (new_level)
14638     {
14639       SaveLevel(level_nr);
14640
14641       num_levels_converted++;
14642
14643       Print("converted.\n");
14644     }
14645     else
14646     {
14647       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14648         levels_failed[level_nr] = TRUE;
14649
14650       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14651     }
14652
14653     num_levels_handled++;
14654   }
14655
14656   Print("\n");
14657   PrintLine("=", 79);
14658   Print("Number of levels handled: %d\n", num_levels_handled);
14659   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14660          (num_levels_handled ?
14661           num_levels_converted * 100 / num_levels_handled : 0));
14662   PrintLine("-", 79);
14663   Print("Summary (for automatic parsing by scripts):\n");
14664   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14665          convert_leveldir->identifier, num_levels_converted,
14666          num_levels_handled,
14667          (num_levels_handled ?
14668           num_levels_converted * 100 / num_levels_handled : 0));
14669
14670   if (num_levels_handled != num_levels_converted)
14671   {
14672     Print(", FAILED:");
14673     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14674       if (levels_failed[i])
14675         Print(" %03d", i);
14676   }
14677
14678   Print("\n");
14679   PrintLine("=", 79);
14680
14681   CloseAllAndExit(0);
14682 }
14683
14684
14685 // ----------------------------------------------------------------------------
14686 // create and save images for use in level sketches (raw BMP format)
14687 // ----------------------------------------------------------------------------
14688
14689 void CreateLevelSketchImages(void)
14690 {
14691   Bitmap *bitmap1;
14692   Bitmap *bitmap2;
14693   int i;
14694
14695   InitElementPropertiesGfxElement();
14696
14697   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14698   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14699
14700   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14701   {
14702     int element = getMappedElement(i);
14703     char basename1[16];
14704     char basename2[16];
14705     char *filename1;
14706     char *filename2;
14707
14708     sprintf(basename1, "%04d.bmp", i);
14709     sprintf(basename2, "%04ds.bmp", i);
14710
14711     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14712     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14713
14714     DrawSizedElement(0, 0, element, TILESIZE);
14715     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14716
14717     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14718       Fail("cannot save level sketch image file '%s'", filename1);
14719
14720     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14721     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14722
14723     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14724       Fail("cannot save level sketch image file '%s'", filename2);
14725
14726     free(filename1);
14727     free(filename2);
14728
14729     // create corresponding SQL statements (for normal and small images)
14730     if (i < 1000)
14731     {
14732       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14733       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14734     }
14735
14736     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14737     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14738
14739     // optional: create content for forum level sketch demonstration post
14740     if (options.debug)
14741       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14742   }
14743
14744   FreeBitmap(bitmap1);
14745   FreeBitmap(bitmap2);
14746
14747   if (options.debug)
14748     fprintf(stderr, "\n");
14749
14750   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14751
14752   CloseAllAndExit(0);
14753 }
14754
14755
14756 // ----------------------------------------------------------------------------
14757 // create and save images for element collecting animations (raw BMP format)
14758 // ----------------------------------------------------------------------------
14759
14760 static boolean createCollectImage(int element)
14761 {
14762   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14763 }
14764
14765 void CreateCollectElementImages(void)
14766 {
14767   int i, j;
14768   int num_steps = 8;
14769   int anim_frames = num_steps - 1;
14770   int tile_size = TILESIZE;
14771   int anim_width  = tile_size * anim_frames;
14772   int anim_height = tile_size;
14773   int num_collect_images = 0;
14774   int pos_collect_images = 0;
14775
14776   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14777     if (createCollectImage(i))
14778       num_collect_images++;
14779
14780   Info("Creating %d element collecting animation images ...",
14781        num_collect_images);
14782
14783   int dst_width  = anim_width * 2;
14784   int dst_height = anim_height * num_collect_images / 2;
14785   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14786   char *basename_bmp = "RocksCollect.bmp";
14787   char *basename_png = "RocksCollect.png";
14788   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14789   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14790   int len_filename_bmp = strlen(filename_bmp);
14791   int len_filename_png = strlen(filename_png);
14792   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14793   char cmd_convert[max_command_len];
14794
14795   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14796            filename_bmp,
14797            filename_png);
14798
14799   // force using RGBA surface for destination bitmap
14800   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14801                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14802
14803   dst_bitmap->surface =
14804     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14805
14806   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14807   {
14808     if (!createCollectImage(i))
14809       continue;
14810
14811     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14812     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14813     int graphic = el2img(i);
14814     char *token_name = element_info[i].token_name;
14815     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14816     Bitmap *src_bitmap;
14817     int src_x, src_y;
14818
14819     Info("- creating collecting image for '%s' ...", token_name);
14820
14821     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14822
14823     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14824                tile_size, tile_size, 0, 0);
14825
14826     // force using RGBA surface for temporary bitmap (using transparent black)
14827     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14828                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14829
14830     tmp_bitmap->surface =
14831       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14832
14833     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14834
14835     for (j = 0; j < anim_frames; j++)
14836     {
14837       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14838       int frame_size = frame_size_final * num_steps;
14839       int offset = (tile_size - frame_size_final) / 2;
14840       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14841
14842       while (frame_size > frame_size_final)
14843       {
14844         frame_size /= 2;
14845
14846         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14847
14848         FreeBitmap(frame_bitmap);
14849
14850         frame_bitmap = half_bitmap;
14851       }
14852
14853       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14854                        frame_size_final, frame_size_final,
14855                        dst_x + j * tile_size + offset, dst_y + offset);
14856
14857       FreeBitmap(frame_bitmap);
14858     }
14859
14860     tmp_bitmap->surface_masked = NULL;
14861
14862     FreeBitmap(tmp_bitmap);
14863
14864     pos_collect_images++;
14865   }
14866
14867   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14868     Fail("cannot save element collecting image file '%s'", filename_bmp);
14869
14870   FreeBitmap(dst_bitmap);
14871
14872   Info("Converting image file from BMP to PNG ...");
14873
14874   if (system(cmd_convert) != 0)
14875     Fail("converting image file failed");
14876
14877   unlink(filename_bmp);
14878
14879   Info("Done.");
14880
14881   CloseAllAndExit(0);
14882 }
14883
14884
14885 // ----------------------------------------------------------------------------
14886 // create and save images for custom and group elements (raw BMP format)
14887 // ----------------------------------------------------------------------------
14888
14889 void CreateCustomElementImages(char *directory)
14890 {
14891   char *src_basename = "RocksCE-template.ilbm";
14892   char *dst_basename = "RocksCE.bmp";
14893   char *src_filename = getPath2(directory, src_basename);
14894   char *dst_filename = getPath2(directory, dst_basename);
14895   Bitmap *src_bitmap;
14896   Bitmap *bitmap;
14897   int yoffset_ce = 0;
14898   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14899   int i;
14900
14901   InitVideoDefaults();
14902
14903   ReCreateBitmap(&backbuffer, video.width, video.height);
14904
14905   src_bitmap = LoadImage(src_filename);
14906
14907   bitmap = CreateBitmap(TILEX * 16 * 2,
14908                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14909                         DEFAULT_DEPTH);
14910
14911   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14912   {
14913     int x = i % 16;
14914     int y = i / 16;
14915     int ii = i + 1;
14916     int j;
14917
14918     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14919                TILEX * x, TILEY * y + yoffset_ce);
14920
14921     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14922                TILEX, TILEY,
14923                TILEX * x + TILEX * 16,
14924                TILEY * y + yoffset_ce);
14925
14926     for (j = 2; j >= 0; j--)
14927     {
14928       int c = ii % 10;
14929
14930       BlitBitmap(src_bitmap, bitmap,
14931                  TILEX + c * 7, 0, 6, 10,
14932                  TILEX * x + 6 + j * 7,
14933                  TILEY * y + 11 + yoffset_ce);
14934
14935       BlitBitmap(src_bitmap, bitmap,
14936                  TILEX + c * 8, TILEY, 6, 10,
14937                  TILEX * 16 + TILEX * x + 6 + j * 8,
14938                  TILEY * y + 10 + yoffset_ce);
14939
14940       ii /= 10;
14941     }
14942   }
14943
14944   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14945   {
14946     int x = i % 16;
14947     int y = i / 16;
14948     int ii = i + 1;
14949     int j;
14950
14951     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14952                TILEX * x, TILEY * y + yoffset_ge);
14953
14954     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14955                TILEX, TILEY,
14956                TILEX * x + TILEX * 16,
14957                TILEY * y + yoffset_ge);
14958
14959     for (j = 1; j >= 0; j--)
14960     {
14961       int c = ii % 10;
14962
14963       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14964                  TILEX * x + 6 + j * 10,
14965                  TILEY * y + 11 + yoffset_ge);
14966
14967       BlitBitmap(src_bitmap, bitmap,
14968                  TILEX + c * 8, TILEY + 12, 6, 10,
14969                  TILEX * 16 + TILEX * x + 10 + j * 8,
14970                  TILEY * y + 10 + yoffset_ge);
14971
14972       ii /= 10;
14973     }
14974   }
14975
14976   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14977     Fail("cannot save CE graphics file '%s'", dst_filename);
14978
14979   FreeBitmap(bitmap);
14980
14981   CloseAllAndExit(0);
14982 }