added option for BD1 magic wall / amoeba bug for native BD engine
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "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_BOOLEAN,                       CONF_VALUE_8_BIT(3),
743     &li.bd_magic_wall_zero_infinite,    TRUE
744   },
745   {
746     EL_BD_MAGIC_WALL,                   -1,
747     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
748     &li.bd_magic_wall_break_scan,       FALSE
749   },
750   {
751     EL_BD_MAGIC_WALL,                   -1,
752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
753     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
754   },
755   {
756     EL_BD_MAGIC_WALL,                   -1,
757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
758     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
759   },
760   {
761     EL_BD_MAGIC_WALL,                   -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
763     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
764   },
765   {
766     EL_BD_MAGIC_WALL,                   -1,
767     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
768     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
769   },
770   {
771     EL_BD_MAGIC_WALL,                   -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
773     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
774   },
775   {
776     EL_BD_MAGIC_WALL,                   -1,
777     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
778     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
779   },
780   {
781     EL_BD_MAGIC_WALL,                   -1,
782     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
783     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
784   },
785
786   {
787     EL_BD_CLOCK,                        -1,
788     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
789     &li.bd_clock_extra_time,            30
790   },
791
792   {
793     EL_BD_VOODOO_DOLL,                  -1,
794     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
795     &li.bd_voodoo_collects_diamonds,    FALSE
796   },
797   {
798     EL_BD_VOODOO_DOLL,                  -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
800     &li.bd_voodoo_hurt_kills_player,    FALSE
801   },
802   {
803     EL_BD_VOODOO_DOLL,                  -1,
804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
805     &li.bd_voodoo_dies_by_rock,         FALSE
806   },
807   {
808     EL_BD_VOODOO_DOLL,                  -1,
809     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
810     &li.bd_voodoo_vanish_by_explosion,  TRUE
811   },
812   {
813     EL_BD_VOODOO_DOLL,                  -1,
814     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
815     &li.bd_voodoo_penalty_time,         30
816   },
817
818   {
819     EL_BD_SLIME,                        -1,
820     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
821     &li.bd_slime_is_predictable,        TRUE
822   },
823   {
824     EL_BD_SLIME,                        -1,
825     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
826     &li.bd_slime_permeability_rate,     100
827   },
828   {
829     EL_BD_SLIME,                        -1,
830     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
831     &li.bd_slime_permeability_bits_c64, 0
832   },
833   {
834     EL_BD_SLIME,                        -1,
835     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
836     &li.bd_slime_random_seed_c64,       -1
837   },
838   {
839     EL_BD_SLIME,                        -1,
840     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
841     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
842   },
843   {
844     EL_BD_SLIME,                        -1,
845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
846     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
847   },
848   {
849     EL_BD_SLIME,                        -1,
850     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
851     &li.bd_slime_eats_element_2,        EL_BD_ROCK
852   },
853   {
854     EL_BD_SLIME,                        -1,
855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
856     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
857   },
858   {
859     EL_BD_SLIME,                        -1,
860     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
861     &li.bd_slime_eats_element_3,        EL_BD_NUT
862   },
863   {
864     EL_BD_SLIME,                        -1,
865     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
866     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
867   },
868
869   {
870     EL_BD_ACID,                         -1,
871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
872     &li.bd_acid_eats_element,           EL_BD_SAND
873   },
874   {
875     EL_BD_ACID,                         -1,
876     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
877     &li.bd_acid_spread_rate,            3
878   },
879   {
880     EL_BD_ACID,                         -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
882     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
883   },
884
885   {
886     EL_BD_BITER,                        -1,
887     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
888     &li.bd_biter_move_delay,            0
889   },
890   {
891     EL_BD_BITER,                        -1,
892     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
893     &li.bd_biter_eats_element,          EL_BD_DIAMOND
894   },
895
896   {
897     EL_BD_BLADDER,                      -1,
898     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
899     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
900   },
901
902   {
903     EL_BD_EXPANDABLE_WALL_ANY,          -1,
904     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
905     &li.bd_change_expanding_wall,       FALSE
906   },
907   {
908     EL_BD_EXPANDABLE_WALL_ANY,          -1,
909     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
910     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
911   },
912
913   {
914     EL_BD_REPLICATOR,                   -1,
915     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
916     &li.bd_replicators_active,          TRUE
917   },
918   {
919     EL_BD_REPLICATOR,                   -1,
920     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
921     &li.bd_replicator_create_delay,     4
922   },
923
924   {
925     EL_BD_CONVEYOR_LEFT,                -1,
926     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
927     &li.bd_conveyor_belts_active,       TRUE
928   },
929   {
930     EL_BD_CONVEYOR_LEFT,                -1,
931     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
932     &li.bd_conveyor_belts_changed,      FALSE
933   },
934
935   {
936     EL_BD_WATER,                        -1,
937     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
938     &li.bd_water_cannot_flow_down,      FALSE
939   },
940
941   {
942     EL_BD_NUT,                          -1,
943     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
944     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
945   },
946
947   {
948     EL_BD_PNEUMATIC_HAMMER,             -1,
949     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
950     &li.bd_hammer_walls_break_delay,    5
951   },
952   {
953     EL_BD_PNEUMATIC_HAMMER,             -1,
954     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
955     &li.bd_hammer_walls_reappear,       FALSE
956   },
957   {
958     EL_BD_PNEUMATIC_HAMMER,             -1,
959     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
960     &li.bd_hammer_walls_reappear_delay, 100
961   },
962
963   {
964     EL_BD_ROCKET_LAUNCHER,              -1,
965     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
966     &li.bd_infinite_rockets,            FALSE
967   },
968
969   {
970     EL_BD_SKELETON,                     -1,
971     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
972     &li.bd_num_skeletons_needed_for_pot, 5
973   },
974   {
975     EL_BD_SKELETON,                     -1,
976     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
977     &li.bd_skeleton_worth_num_diamonds, 0
978   },
979
980   {
981     EL_BD_CREATURE_SWITCH,              -1,
982     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
983     &li.bd_creatures_start_backwards,   FALSE
984   },
985   {
986     EL_BD_CREATURE_SWITCH,              -1,
987     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
988     &li.bd_creatures_turn_on_hatching,  FALSE
989   },
990   {
991     EL_BD_CREATURE_SWITCH,              -1,
992     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
993     &li.bd_creatures_auto_turn_delay,   0
994   },
995
996   {
997     EL_BD_GRAVITY_SWITCH,               -1,
998     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
999     &li.bd_gravity_direction,           GD_MV_DOWN
1000   },
1001   {
1002     EL_BD_GRAVITY_SWITCH,               -1,
1003     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1004     &li.bd_gravity_switch_active,       FALSE
1005   },
1006   {
1007     EL_BD_GRAVITY_SWITCH,               -1,
1008     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1009     &li.bd_gravity_switch_delay,        10
1010   },
1011   {
1012     EL_BD_GRAVITY_SWITCH,               -1,
1013     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1014     &li.bd_gravity_affects_all,         TRUE
1015   },
1016
1017   // (the following values are related to various game elements)
1018
1019   {
1020     EL_EMERALD,                         -1,
1021     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1022     &li.score[SC_EMERALD],              10
1023   },
1024
1025   {
1026     EL_DIAMOND,                         -1,
1027     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1028     &li.score[SC_DIAMOND],              10
1029   },
1030
1031   {
1032     EL_BUG,                             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.score[SC_BUG],                  10
1035   },
1036
1037   {
1038     EL_SPACESHIP,                       -1,
1039     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1040     &li.score[SC_SPACESHIP],            10
1041   },
1042
1043   {
1044     EL_PACMAN,                          -1,
1045     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1046     &li.score[SC_PACMAN],               10
1047   },
1048
1049   {
1050     EL_NUT,                             -1,
1051     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1052     &li.score[SC_NUT],                  10
1053   },
1054
1055   {
1056     EL_DYNAMITE,                        -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.score[SC_DYNAMITE],             10
1059   },
1060
1061   {
1062     EL_KEY_1,                           -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.score[SC_KEY],                  10
1065   },
1066
1067   {
1068     EL_PEARL,                           -1,
1069     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1070     &li.score[SC_PEARL],                10
1071   },
1072
1073   {
1074     EL_CRYSTAL,                         -1,
1075     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1076     &li.score[SC_CRYSTAL],              10
1077   },
1078
1079   // (amoeba values used by R'n'D game engine only)
1080   {
1081     EL_BD_AMOEBA,                       -1,
1082     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1083     &li.amoeba_content,                 EL_DIAMOND
1084   },
1085   {
1086     EL_BD_AMOEBA,                       -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1088     &li.amoeba_speed,                   10
1089   },
1090   {
1091     EL_BD_AMOEBA,                       -1,
1092     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1093     &li.grow_into_diggable,             TRUE
1094   },
1095   // (amoeba values used by BD game engine only)
1096   {
1097     EL_BD_AMOEBA,                       -1,
1098     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1099     &li.bd_amoeba_wait_for_hatching,    FALSE
1100   },
1101   {
1102     EL_BD_AMOEBA,                       -1,
1103     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1104     &li.bd_amoeba_start_immediately,    TRUE
1105   },
1106   {
1107     EL_BD_AMOEBA,                       -1,
1108     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1109     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1110   },
1111   {
1112     EL_BD_AMOEBA,                       -1,
1113     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1114     &li.bd_amoeba_threshold_too_big,    200
1115   },
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1119     &li.bd_amoeba_slow_growth_time,     200
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1124     &li.bd_amoeba_slow_growth_rate,     3
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1129     &li.bd_amoeba_fast_growth_rate,     25
1130   },
1131   {
1132     EL_BD_AMOEBA,                       -1,
1133     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1134     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1135   },
1136   {
1137     EL_BD_AMOEBA,                       -1,
1138     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1139     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1140   },
1141
1142   {
1143     EL_BD_AMOEBA_2,                     -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1145     &li.bd_amoeba_2_threshold_too_big,  200
1146   },
1147   {
1148     EL_BD_AMOEBA_2,                     -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1150     &li.bd_amoeba_2_slow_growth_time,   200
1151   },
1152   {
1153     EL_BD_AMOEBA_2,                     -1,
1154     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1155     &li.bd_amoeba_2_slow_growth_rate,   3
1156   },
1157   {
1158     EL_BD_AMOEBA_2,                     -1,
1159     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1160     &li.bd_amoeba_2_fast_growth_rate,   25
1161   },
1162   {
1163     EL_BD_AMOEBA_2,                     -1,
1164     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1165     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1166   },
1167   {
1168     EL_BD_AMOEBA_2,                     -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1170     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1171   },
1172   {
1173     EL_BD_AMOEBA_2,                     -1,
1174     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1175     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1176   },
1177   {
1178     EL_BD_AMOEBA_2,                     -1,
1179     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1180     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1181   },
1182
1183   {
1184     EL_YAMYAM,                          -1,
1185     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1186     &li.yamyam_content,                 EL_ROCK, NULL,
1187     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1188   },
1189   {
1190     EL_YAMYAM,                          -1,
1191     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1192     &li.score[SC_YAMYAM],               10
1193   },
1194
1195   {
1196     EL_ROBOT,                           -1,
1197     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1198     &li.score[SC_ROBOT],                10
1199   },
1200   {
1201     EL_ROBOT,                           -1,
1202     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1203     &li.slurp_score,                    10
1204   },
1205
1206   {
1207     EL_ROBOT_WHEEL,                     -1,
1208     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1209     &li.time_wheel,                     10
1210   },
1211
1212   {
1213     EL_MAGIC_WALL,                      -1,
1214     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1215     &li.time_magic_wall,                10
1216   },
1217
1218   {
1219     EL_GAME_OF_LIFE,                    -1,
1220     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1221     &li.game_of_life[0],                2
1222   },
1223   {
1224     EL_GAME_OF_LIFE,                    -1,
1225     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1226     &li.game_of_life[1],                3
1227   },
1228   {
1229     EL_GAME_OF_LIFE,                    -1,
1230     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1231     &li.game_of_life[2],                3
1232   },
1233   {
1234     EL_GAME_OF_LIFE,                    -1,
1235     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1236     &li.game_of_life[3],                3
1237   },
1238   {
1239     EL_GAME_OF_LIFE,                    -1,
1240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1241     &li.use_life_bugs,                  FALSE
1242   },
1243
1244   {
1245     EL_BIOMAZE,                         -1,
1246     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1247     &li.biomaze[0],                     2
1248   },
1249   {
1250     EL_BIOMAZE,                         -1,
1251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1252     &li.biomaze[1],                     3
1253   },
1254   {
1255     EL_BIOMAZE,                         -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1257     &li.biomaze[2],                     3
1258   },
1259   {
1260     EL_BIOMAZE,                         -1,
1261     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1262     &li.biomaze[3],                     3
1263   },
1264
1265   {
1266     EL_TIMEGATE_SWITCH,                 -1,
1267     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1268     &li.time_timegate,                  10
1269   },
1270
1271   {
1272     EL_LIGHT_SWITCH_ACTIVE,             -1,
1273     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1274     &li.time_light,                     10
1275   },
1276
1277   {
1278     EL_SHIELD_NORMAL,                   -1,
1279     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1280     &li.shield_normal_time,             10
1281   },
1282   {
1283     EL_SHIELD_NORMAL,                   -1,
1284     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1285     &li.score[SC_SHIELD],               10
1286   },
1287
1288   {
1289     EL_SHIELD_DEADLY,                   -1,
1290     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1291     &li.shield_deadly_time,             10
1292   },
1293   {
1294     EL_SHIELD_DEADLY,                   -1,
1295     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1296     &li.score[SC_SHIELD],               10
1297   },
1298
1299   {
1300     EL_EXTRA_TIME,                      -1,
1301     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1302     &li.extra_time,                     10
1303   },
1304   {
1305     EL_EXTRA_TIME,                      -1,
1306     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1307     &li.extra_time_score,               10
1308   },
1309
1310   {
1311     EL_TIME_ORB_FULL,                   -1,
1312     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1313     &li.time_orb_time,                  10
1314   },
1315   {
1316     EL_TIME_ORB_FULL,                   -1,
1317     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1318     &li.use_time_orb_bug,               FALSE
1319   },
1320
1321   {
1322     EL_SPRING,                          -1,
1323     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1324     &li.use_spring_bug,                 FALSE
1325   },
1326
1327   {
1328     EL_EMC_ANDROID,                     -1,
1329     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1330     &li.android_move_time,              10
1331   },
1332   {
1333     EL_EMC_ANDROID,                     -1,
1334     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1335     &li.android_clone_time,             10
1336   },
1337   {
1338     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1339     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1340     &li.android_clone_element[0],       EL_EMPTY, NULL,
1341     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1342   },
1343   {
1344     EL_EMC_ANDROID,                     -1,
1345     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1346     &li.android_clone_element[0],       EL_EMPTY, NULL,
1347     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1348   },
1349
1350   {
1351     EL_EMC_LENSES,                      -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.lenses_score,                   10
1354   },
1355   {
1356     EL_EMC_LENSES,                      -1,
1357     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1358     &li.lenses_time,                    10
1359   },
1360
1361   {
1362     EL_EMC_MAGNIFIER,                   -1,
1363     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1364     &li.magnify_score,                  10
1365   },
1366   {
1367     EL_EMC_MAGNIFIER,                   -1,
1368     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1369     &li.magnify_time,                   10
1370   },
1371
1372   {
1373     EL_EMC_MAGIC_BALL,                  -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1375     &li.ball_time,                      10
1376   },
1377   {
1378     EL_EMC_MAGIC_BALL,                  -1,
1379     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1380     &li.ball_random,                    FALSE
1381   },
1382   {
1383     EL_EMC_MAGIC_BALL,                  -1,
1384     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1385     &li.ball_active_initial,            FALSE
1386   },
1387   {
1388     EL_EMC_MAGIC_BALL,                  -1,
1389     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1390     &li.ball_content,                   EL_EMPTY, NULL,
1391     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1392   },
1393
1394   {
1395     EL_SOKOBAN_FIELD_EMPTY,             -1,
1396     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1397     &li.sb_fields_needed,               TRUE
1398   },
1399
1400   {
1401     EL_SOKOBAN_OBJECT,                  -1,
1402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1403     &li.sb_objects_needed,              TRUE
1404   },
1405
1406   {
1407     EL_MM_MCDUFFIN,                     -1,
1408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1409     &li.mm_laser_red,                   FALSE
1410   },
1411   {
1412     EL_MM_MCDUFFIN,                     -1,
1413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1414     &li.mm_laser_green,                 FALSE
1415   },
1416   {
1417     EL_MM_MCDUFFIN,                     -1,
1418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1419     &li.mm_laser_blue,                  TRUE
1420   },
1421
1422   {
1423     EL_DF_LASER,                        -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1425     &li.df_laser_red,                   TRUE
1426   },
1427   {
1428     EL_DF_LASER,                        -1,
1429     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1430     &li.df_laser_green,                 TRUE
1431   },
1432   {
1433     EL_DF_LASER,                        -1,
1434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1435     &li.df_laser_blue,                  FALSE
1436   },
1437
1438   {
1439     EL_MM_FUSE_ACTIVE,                  -1,
1440     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1441     &li.mm_time_fuse,                   25
1442   },
1443   {
1444     EL_MM_BOMB,                         -1,
1445     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1446     &li.mm_time_bomb,                   75
1447   },
1448
1449   {
1450     EL_MM_GRAY_BALL,                    -1,
1451     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1452     &li.mm_time_ball,                   75
1453   },
1454   {
1455     EL_MM_GRAY_BALL,                    -1,
1456     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1457     &li.mm_ball_choice_mode,            ANIM_RANDOM
1458   },
1459   {
1460     EL_MM_GRAY_BALL,                    -1,
1461     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1462     &li.mm_ball_content,                EL_EMPTY, NULL,
1463     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1464   },
1465   {
1466     EL_MM_GRAY_BALL,                    -1,
1467     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1468     &li.rotate_mm_ball_content,         TRUE
1469   },
1470   {
1471     EL_MM_GRAY_BALL,                    -1,
1472     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1473     &li.explode_mm_ball,                FALSE
1474   },
1475
1476   {
1477     EL_MM_STEEL_BLOCK,                  -1,
1478     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1479     &li.mm_time_block,                  75
1480   },
1481   {
1482     EL_MM_LIGHTBALL,                    -1,
1483     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1484     &li.score[SC_ELEM_BONUS],           10
1485   },
1486
1487   {
1488     -1,                                 -1,
1489     -1,                                 -1,
1490     NULL,                               -1
1491   }
1492 };
1493
1494 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1495 {
1496   {
1497     -1,                                 -1,
1498     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1499     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1500   },
1501   {
1502     -1,                                 -1,
1503     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1504     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1505   },
1506
1507   {
1508     -1,                                 -1,
1509     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1510     &xx_envelope.autowrap,              FALSE
1511   },
1512   {
1513     -1,                                 -1,
1514     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1515     &xx_envelope.centered,              FALSE
1516   },
1517
1518   {
1519     -1,                                 -1,
1520     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1521     &xx_envelope.text,                  -1, NULL,
1522     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1523     &xx_default_string_empty[0]
1524   },
1525
1526   {
1527     -1,                                 -1,
1528     -1,                                 -1,
1529     NULL,                               -1
1530   }
1531 };
1532
1533 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1534 {
1535   {
1536     -1,                                 -1,
1537     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1538     &xx_ei.description[0],              -1,
1539     &yy_ei.description[0],
1540     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1541     &xx_default_description[0]
1542   },
1543
1544   {
1545     -1,                                 -1,
1546     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1547     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1548     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1549   },
1550 #if ENABLE_RESERVED_CODE
1551   // (reserved for later use)
1552   {
1553     -1,                                 -1,
1554     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1555     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1556     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1557   },
1558 #endif
1559
1560   {
1561     -1,                                 -1,
1562     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1563     &xx_ei.use_gfx_element,             FALSE,
1564     &yy_ei.use_gfx_element
1565   },
1566   {
1567     -1,                                 -1,
1568     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1569     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1570     &yy_ei.gfx_element_initial
1571   },
1572
1573   {
1574     -1,                                 -1,
1575     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1576     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1577     &yy_ei.access_direction
1578   },
1579
1580   {
1581     -1,                                 -1,
1582     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1583     &xx_ei.collect_score_initial,       10,
1584     &yy_ei.collect_score_initial
1585   },
1586   {
1587     -1,                                 -1,
1588     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1589     &xx_ei.collect_count_initial,       1,
1590     &yy_ei.collect_count_initial
1591   },
1592
1593   {
1594     -1,                                 -1,
1595     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1596     &xx_ei.ce_value_fixed_initial,      0,
1597     &yy_ei.ce_value_fixed_initial
1598   },
1599   {
1600     -1,                                 -1,
1601     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1602     &xx_ei.ce_value_random_initial,     0,
1603     &yy_ei.ce_value_random_initial
1604   },
1605   {
1606     -1,                                 -1,
1607     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1608     &xx_ei.use_last_ce_value,           FALSE,
1609     &yy_ei.use_last_ce_value
1610   },
1611
1612   {
1613     -1,                                 -1,
1614     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1615     &xx_ei.push_delay_fixed,            8,
1616     &yy_ei.push_delay_fixed
1617   },
1618   {
1619     -1,                                 -1,
1620     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1621     &xx_ei.push_delay_random,           8,
1622     &yy_ei.push_delay_random
1623   },
1624   {
1625     -1,                                 -1,
1626     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1627     &xx_ei.drop_delay_fixed,            0,
1628     &yy_ei.drop_delay_fixed
1629   },
1630   {
1631     -1,                                 -1,
1632     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1633     &xx_ei.drop_delay_random,           0,
1634     &yy_ei.drop_delay_random
1635   },
1636   {
1637     -1,                                 -1,
1638     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1639     &xx_ei.move_delay_fixed,            0,
1640     &yy_ei.move_delay_fixed
1641   },
1642   {
1643     -1,                                 -1,
1644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1645     &xx_ei.move_delay_random,           0,
1646     &yy_ei.move_delay_random
1647   },
1648   {
1649     -1,                                 -1,
1650     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1651     &xx_ei.step_delay_fixed,            0,
1652     &yy_ei.step_delay_fixed
1653   },
1654   {
1655     -1,                                 -1,
1656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1657     &xx_ei.step_delay_random,           0,
1658     &yy_ei.step_delay_random
1659   },
1660
1661   {
1662     -1,                                 -1,
1663     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1664     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1665     &yy_ei.move_pattern
1666   },
1667   {
1668     -1,                                 -1,
1669     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1670     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1671     &yy_ei.move_direction_initial
1672   },
1673   {
1674     -1,                                 -1,
1675     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1676     &xx_ei.move_stepsize,               TILEX / 8,
1677     &yy_ei.move_stepsize
1678   },
1679
1680   {
1681     -1,                                 -1,
1682     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1683     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1684     &yy_ei.move_enter_element
1685   },
1686   {
1687     -1,                                 -1,
1688     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1689     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1690     &yy_ei.move_leave_element
1691   },
1692   {
1693     -1,                                 -1,
1694     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1695     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1696     &yy_ei.move_leave_type
1697   },
1698
1699   {
1700     -1,                                 -1,
1701     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1702     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1703     &yy_ei.slippery_type
1704   },
1705
1706   {
1707     -1,                                 -1,
1708     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1709     &xx_ei.explosion_type,              EXPLODES_3X3,
1710     &yy_ei.explosion_type
1711   },
1712   {
1713     -1,                                 -1,
1714     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1715     &xx_ei.explosion_delay,             16,
1716     &yy_ei.explosion_delay
1717   },
1718   {
1719     -1,                                 -1,
1720     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1721     &xx_ei.ignition_delay,              8,
1722     &yy_ei.ignition_delay
1723   },
1724
1725   {
1726     -1,                                 -1,
1727     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1728     &xx_ei.content,                     EL_EMPTY_SPACE,
1729     &yy_ei.content,
1730     &xx_num_contents,                   1, 1
1731   },
1732
1733   // ---------- "num_change_pages" must be the last entry ---------------------
1734
1735   {
1736     -1,                                 SAVE_CONF_ALWAYS,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1738     &xx_ei.num_change_pages,            1,
1739     &yy_ei.num_change_pages
1740   },
1741
1742   {
1743     -1,                                 -1,
1744     -1,                                 -1,
1745     NULL,                               -1,
1746     NULL
1747   }
1748 };
1749
1750 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1751 {
1752   // ---------- "current_change_page" must be the first entry -----------------
1753
1754   {
1755     -1,                                 SAVE_CONF_ALWAYS,
1756     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1757     &xx_current_change_page,            -1
1758   },
1759
1760   // ---------- (the remaining entries can be in any order) -------------------
1761
1762   {
1763     -1,                                 -1,
1764     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1765     &xx_change.can_change,              FALSE
1766   },
1767
1768   {
1769     -1,                                 -1,
1770     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1771     &xx_event_bits[0],                  0
1772   },
1773   {
1774     -1,                                 -1,
1775     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1776     &xx_event_bits[1],                  0
1777   },
1778
1779   {
1780     -1,                                 -1,
1781     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1782     &xx_change.trigger_player,          CH_PLAYER_ANY
1783   },
1784   {
1785     -1,                                 -1,
1786     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1787     &xx_change.trigger_side,            CH_SIDE_ANY
1788   },
1789   {
1790     -1,                                 -1,
1791     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1792     &xx_change.trigger_page,            CH_PAGE_ANY
1793   },
1794
1795   {
1796     -1,                                 -1,
1797     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1798     &xx_change.target_element,          EL_EMPTY_SPACE
1799   },
1800
1801   {
1802     -1,                                 -1,
1803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1804     &xx_change.delay_fixed,             0
1805   },
1806   {
1807     -1,                                 -1,
1808     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1809     &xx_change.delay_random,            0
1810   },
1811   {
1812     -1,                                 -1,
1813     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1814     &xx_change.delay_frames,            FRAMES_PER_SECOND
1815   },
1816
1817   {
1818     -1,                                 -1,
1819     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1820     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1821   },
1822
1823   {
1824     -1,                                 -1,
1825     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1826     &xx_change.explode,                 FALSE
1827   },
1828   {
1829     -1,                                 -1,
1830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1831     &xx_change.use_target_content,      FALSE
1832   },
1833   {
1834     -1,                                 -1,
1835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1836     &xx_change.only_if_complete,        FALSE
1837   },
1838   {
1839     -1,                                 -1,
1840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1841     &xx_change.use_random_replace,      FALSE
1842   },
1843   {
1844     -1,                                 -1,
1845     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1846     &xx_change.random_percentage,       100
1847   },
1848   {
1849     -1,                                 -1,
1850     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1851     &xx_change.replace_when,            CP_WHEN_EMPTY
1852   },
1853
1854   {
1855     -1,                                 -1,
1856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1857     &xx_change.has_action,              FALSE
1858   },
1859   {
1860     -1,                                 -1,
1861     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1862     &xx_change.action_type,             CA_NO_ACTION
1863   },
1864   {
1865     -1,                                 -1,
1866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1867     &xx_change.action_mode,             CA_MODE_UNDEFINED
1868   },
1869   {
1870     -1,                                 -1,
1871     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1872     &xx_change.action_arg,              CA_ARG_UNDEFINED
1873   },
1874
1875   {
1876     -1,                                 -1,
1877     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1878     &xx_change.action_element,          EL_EMPTY_SPACE
1879   },
1880
1881   {
1882     -1,                                 -1,
1883     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1884     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1885     &xx_num_contents,                   1, 1
1886   },
1887
1888   {
1889     -1,                                 -1,
1890     -1,                                 -1,
1891     NULL,                               -1
1892   }
1893 };
1894
1895 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1896 {
1897   {
1898     -1,                                 -1,
1899     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1900     &xx_ei.description[0],              -1, NULL,
1901     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1902     &xx_default_description[0]
1903   },
1904
1905   {
1906     -1,                                 -1,
1907     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1908     &xx_ei.use_gfx_element,             FALSE
1909   },
1910   {
1911     -1,                                 -1,
1912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1913     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1914   },
1915
1916   {
1917     -1,                                 -1,
1918     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1919     &xx_group.choice_mode,              ANIM_RANDOM
1920   },
1921
1922   {
1923     -1,                                 -1,
1924     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1925     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1926     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1927   },
1928
1929   {
1930     -1,                                 -1,
1931     -1,                                 -1,
1932     NULL,                               -1
1933   }
1934 };
1935
1936 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1937 {
1938   {
1939     -1,                                 -1,
1940     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1941     &xx_ei.use_gfx_element,             FALSE
1942   },
1943   {
1944     -1,                                 -1,
1945     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1946     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1947   },
1948
1949   {
1950     -1,                                 -1,
1951     -1,                                 -1,
1952     NULL,                               -1
1953   }
1954 };
1955
1956 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1957 {
1958   {
1959     EL_PLAYER_1,                        -1,
1960     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1961     &li.block_snap_field,               TRUE
1962   },
1963   {
1964     EL_PLAYER_1,                        -1,
1965     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1966     &li.continuous_snapping,            TRUE
1967   },
1968   {
1969     EL_PLAYER_1,                        -1,
1970     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1971     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1972   },
1973   {
1974     EL_PLAYER_1,                        -1,
1975     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1976     &li.use_start_element[0],           FALSE
1977   },
1978   {
1979     EL_PLAYER_1,                        -1,
1980     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1981     &li.start_element[0],               EL_PLAYER_1
1982   },
1983   {
1984     EL_PLAYER_1,                        -1,
1985     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1986     &li.use_artwork_element[0],         FALSE
1987   },
1988   {
1989     EL_PLAYER_1,                        -1,
1990     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1991     &li.artwork_element[0],             EL_PLAYER_1
1992   },
1993   {
1994     EL_PLAYER_1,                        -1,
1995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1996     &li.use_explosion_element[0],       FALSE
1997   },
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2001     &li.explosion_element[0],           EL_PLAYER_1
2002   },
2003
2004   {
2005     -1,                                 -1,
2006     -1,                                 -1,
2007     NULL,                               -1
2008   }
2009 };
2010
2011 static struct
2012 {
2013   int filetype;
2014   char *id;
2015 }
2016 filetype_id_list[] =
2017 {
2018   { LEVEL_FILE_TYPE_RND,        "RND"   },
2019   { LEVEL_FILE_TYPE_BD,         "BD"    },
2020   { LEVEL_FILE_TYPE_EM,         "EM"    },
2021   { LEVEL_FILE_TYPE_SP,         "SP"    },
2022   { LEVEL_FILE_TYPE_DX,         "DX"    },
2023   { LEVEL_FILE_TYPE_SB,         "SB"    },
2024   { LEVEL_FILE_TYPE_DC,         "DC"    },
2025   { LEVEL_FILE_TYPE_MM,         "MM"    },
2026   { LEVEL_FILE_TYPE_MM,         "DF"    },
2027   { -1,                         NULL    },
2028 };
2029
2030
2031 // ============================================================================
2032 // level file functions
2033 // ============================================================================
2034
2035 static boolean check_special_flags(char *flag)
2036 {
2037   if (strEqual(options.special_flags, flag) ||
2038       strEqual(leveldir_current->special_flags, flag))
2039     return TRUE;
2040
2041   return FALSE;
2042 }
2043
2044 static struct DateInfo getCurrentDate(void)
2045 {
2046   time_t epoch_seconds = time(NULL);
2047   struct tm *now = localtime(&epoch_seconds);
2048   struct DateInfo date;
2049
2050   date.year  = now->tm_year + 1900;
2051   date.month = now->tm_mon  + 1;
2052   date.day   = now->tm_mday;
2053
2054   date.src   = DATE_SRC_CLOCK;
2055
2056   return date;
2057 }
2058
2059 static void resetEventFlags(struct ElementChangeInfo *change)
2060 {
2061   int i;
2062
2063   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2064     change->has_event[i] = FALSE;
2065 }
2066
2067 static void resetEventBits(void)
2068 {
2069   int i;
2070
2071   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2072     xx_event_bits[i] = 0;
2073 }
2074
2075 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2076 {
2077   int i;
2078
2079   /* important: only change event flag if corresponding event bit is set
2080      (this is because all xx_event_bits[] values are loaded separately,
2081      and all xx_event_bits[] values are set back to zero before loading
2082      another value xx_event_bits[x] (each value representing 32 flags)) */
2083
2084   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2085     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2086       change->has_event[i] = TRUE;
2087 }
2088
2089 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2090 {
2091   int i;
2092
2093   /* in contrast to the above function setEventFlagsFromEventBits(), it
2094      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2095      depending on the corresponding change->has_event[i] values here, as
2096      all xx_event_bits[] values are reset in resetEventBits() before */
2097
2098   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2099     if (change->has_event[i])
2100       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2101 }
2102
2103 static char *getDefaultElementDescription(struct ElementInfo *ei)
2104 {
2105   static char description[MAX_ELEMENT_NAME_LEN + 1];
2106   char *default_description = (ei->custom_description != NULL ?
2107                                ei->custom_description :
2108                                ei->editor_description);
2109   int i;
2110
2111   // always start with reliable default values
2112   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2113     description[i] = '\0';
2114
2115   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2116   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2117
2118   return &description[0];
2119 }
2120
2121 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2122 {
2123   char *default_description = getDefaultElementDescription(ei);
2124   int i;
2125
2126   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2127     ei->description[i] = default_description[i];
2128 }
2129
2130 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2131 {
2132   int i;
2133
2134   for (i = 0; conf[i].data_type != -1; i++)
2135   {
2136     int default_value = conf[i].default_value;
2137     int data_type = conf[i].data_type;
2138     int conf_type = conf[i].conf_type;
2139     int byte_mask = conf_type & CONF_MASK_BYTES;
2140
2141     if (byte_mask == CONF_MASK_MULTI_BYTES)
2142     {
2143       int default_num_entities = conf[i].default_num_entities;
2144       int max_num_entities = conf[i].max_num_entities;
2145
2146       *(int *)(conf[i].num_entities) = default_num_entities;
2147
2148       if (data_type == TYPE_STRING)
2149       {
2150         char *default_string = conf[i].default_string;
2151         char *string = (char *)(conf[i].value);
2152
2153         strncpy(string, default_string, max_num_entities);
2154       }
2155       else if (data_type == TYPE_ELEMENT_LIST)
2156       {
2157         int *element_array = (int *)(conf[i].value);
2158         int j;
2159
2160         for (j = 0; j < max_num_entities; j++)
2161           element_array[j] = default_value;
2162       }
2163       else if (data_type == TYPE_CONTENT_LIST)
2164       {
2165         struct Content *content = (struct Content *)(conf[i].value);
2166         int c, x, y;
2167
2168         for (c = 0; c < max_num_entities; c++)
2169           for (y = 0; y < 3; y++)
2170             for (x = 0; x < 3; x++)
2171               content[c].e[x][y] = default_value;
2172       }
2173     }
2174     else        // constant size configuration data (1, 2 or 4 bytes)
2175     {
2176       if (data_type == TYPE_BOOLEAN)
2177         *(boolean *)(conf[i].value) = default_value;
2178       else
2179         *(int *)    (conf[i].value) = default_value;
2180     }
2181   }
2182 }
2183
2184 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2185 {
2186   int i;
2187
2188   for (i = 0; conf[i].data_type != -1; i++)
2189   {
2190     int data_type = conf[i].data_type;
2191     int conf_type = conf[i].conf_type;
2192     int byte_mask = conf_type & CONF_MASK_BYTES;
2193
2194     if (byte_mask == CONF_MASK_MULTI_BYTES)
2195     {
2196       int max_num_entities = conf[i].max_num_entities;
2197
2198       if (data_type == TYPE_STRING)
2199       {
2200         char *string      = (char *)(conf[i].value);
2201         char *string_copy = (char *)(conf[i].value_copy);
2202
2203         strncpy(string_copy, string, max_num_entities);
2204       }
2205       else if (data_type == TYPE_ELEMENT_LIST)
2206       {
2207         int *element_array      = (int *)(conf[i].value);
2208         int *element_array_copy = (int *)(conf[i].value_copy);
2209         int j;
2210
2211         for (j = 0; j < max_num_entities; j++)
2212           element_array_copy[j] = element_array[j];
2213       }
2214       else if (data_type == TYPE_CONTENT_LIST)
2215       {
2216         struct Content *content      = (struct Content *)(conf[i].value);
2217         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2218         int c, x, y;
2219
2220         for (c = 0; c < max_num_entities; c++)
2221           for (y = 0; y < 3; y++)
2222             for (x = 0; x < 3; x++)
2223               content_copy[c].e[x][y] = content[c].e[x][y];
2224       }
2225     }
2226     else        // constant size configuration data (1, 2 or 4 bytes)
2227     {
2228       if (data_type == TYPE_BOOLEAN)
2229         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2230       else
2231         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2232     }
2233   }
2234 }
2235
2236 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2237 {
2238   int i;
2239
2240   xx_ei = *ei_from;     // copy element data into temporary buffer
2241   yy_ei = *ei_to;       // copy element data into temporary buffer
2242
2243   copyConfigFromConfigList(chunk_config_CUSX_base);
2244
2245   *ei_from = xx_ei;
2246   *ei_to   = yy_ei;
2247
2248   // ---------- reinitialize and copy change pages ----------
2249
2250   ei_to->num_change_pages = ei_from->num_change_pages;
2251   ei_to->current_change_page = ei_from->current_change_page;
2252
2253   setElementChangePages(ei_to, ei_to->num_change_pages);
2254
2255   for (i = 0; i < ei_to->num_change_pages; i++)
2256     ei_to->change_page[i] = ei_from->change_page[i];
2257
2258   // ---------- copy group element info ----------
2259   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2260     *ei_to->group = *ei_from->group;
2261
2262   // mark this custom element as modified
2263   ei_to->modified_settings = TRUE;
2264 }
2265
2266 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2267 {
2268   int change_page_size = sizeof(struct ElementChangeInfo);
2269
2270   ei->num_change_pages = MAX(1, change_pages);
2271
2272   ei->change_page =
2273     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2274
2275   if (ei->current_change_page >= ei->num_change_pages)
2276     ei->current_change_page = ei->num_change_pages - 1;
2277
2278   ei->change = &ei->change_page[ei->current_change_page];
2279 }
2280
2281 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2282 {
2283   xx_change = *change;          // copy change data into temporary buffer
2284
2285   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2286
2287   *change = xx_change;
2288
2289   resetEventFlags(change);
2290
2291   change->direct_action = 0;
2292   change->other_action = 0;
2293
2294   change->pre_change_function = NULL;
2295   change->change_function = NULL;
2296   change->post_change_function = NULL;
2297 }
2298
2299 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2300 {
2301   int i, x, y;
2302
2303   li = *level;          // copy level data into temporary buffer
2304   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2305   *level = li;          // copy temporary buffer back to level data
2306
2307   setLevelInfoToDefaults_BD();
2308   setLevelInfoToDefaults_EM();
2309   setLevelInfoToDefaults_SP();
2310   setLevelInfoToDefaults_MM();
2311
2312   level->native_bd_level = &native_bd_level;
2313   level->native_em_level = &native_em_level;
2314   level->native_sp_level = &native_sp_level;
2315   level->native_mm_level = &native_mm_level;
2316
2317   level->file_version = FILE_VERSION_ACTUAL;
2318   level->game_version = GAME_VERSION_ACTUAL;
2319
2320   level->creation_date = getCurrentDate();
2321
2322   level->encoding_16bit_field  = TRUE;
2323   level->encoding_16bit_yamyam = TRUE;
2324   level->encoding_16bit_amoeba = TRUE;
2325
2326   // clear level name and level author string buffers
2327   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2328     level->name[i] = '\0';
2329   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2330     level->author[i] = '\0';
2331
2332   // set level name and level author to default values
2333   strcpy(level->name, NAMELESS_LEVEL_NAME);
2334   strcpy(level->author, ANONYMOUS_NAME);
2335
2336   // set level playfield to playable default level with player and exit
2337   for (x = 0; x < MAX_LEV_FIELDX; x++)
2338     for (y = 0; y < MAX_LEV_FIELDY; y++)
2339       level->field[x][y] = EL_SAND;
2340
2341   level->field[0][0] = EL_PLAYER_1;
2342   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2343
2344   BorderElement = EL_STEELWALL;
2345
2346   // detect custom elements when loading them
2347   level->file_has_custom_elements = FALSE;
2348
2349   // set all bug compatibility flags to "false" => do not emulate this bug
2350   level->use_action_after_change_bug = FALSE;
2351
2352   if (leveldir_current)
2353   {
2354     // try to determine better author name than 'anonymous'
2355     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2356     {
2357       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2358       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2359     }
2360     else
2361     {
2362       switch (LEVELCLASS(leveldir_current))
2363       {
2364         case LEVELCLASS_TUTORIAL:
2365           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2366           break;
2367
2368         case LEVELCLASS_CONTRIB:
2369           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2370           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2371           break;
2372
2373         case LEVELCLASS_PRIVATE:
2374           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2375           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2376           break;
2377
2378         default:
2379           // keep default value
2380           break;
2381       }
2382     }
2383   }
2384 }
2385
2386 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2387 {
2388   static boolean clipboard_elements_initialized = FALSE;
2389   int i;
2390
2391   InitElementPropertiesStatic();
2392
2393   li = *level;          // copy level data into temporary buffer
2394   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2395   *level = li;          // copy temporary buffer back to level data
2396
2397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2398   {
2399     int element = i;
2400     struct ElementInfo *ei = &element_info[element];
2401
2402     if (element == EL_MM_GRAY_BALL)
2403     {
2404       struct LevelInfo_MM *level_mm = level->native_mm_level;
2405       int j;
2406
2407       for (j = 0; j < level->num_mm_ball_contents; j++)
2408         level->mm_ball_content[j] =
2409           map_element_MM_to_RND(level_mm->ball_content[j]);
2410     }
2411
2412     // never initialize clipboard elements after the very first time
2413     // (to be able to use clipboard elements between several levels)
2414     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2415       continue;
2416
2417     if (IS_ENVELOPE(element))
2418     {
2419       int envelope_nr = element - EL_ENVELOPE_1;
2420
2421       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2422
2423       level->envelope[envelope_nr] = xx_envelope;
2424     }
2425
2426     if (IS_CUSTOM_ELEMENT(element) ||
2427         IS_GROUP_ELEMENT(element) ||
2428         IS_INTERNAL_ELEMENT(element))
2429     {
2430       xx_ei = *ei;      // copy element data into temporary buffer
2431
2432       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2433
2434       *ei = xx_ei;
2435     }
2436
2437     setElementChangePages(ei, 1);
2438     setElementChangeInfoToDefaults(ei->change);
2439
2440     if (IS_CUSTOM_ELEMENT(element) ||
2441         IS_GROUP_ELEMENT(element))
2442     {
2443       setElementDescriptionToDefault(ei);
2444
2445       ei->modified_settings = FALSE;
2446     }
2447
2448     if (IS_CUSTOM_ELEMENT(element) ||
2449         IS_INTERNAL_ELEMENT(element))
2450     {
2451       // internal values used in level editor
2452
2453       ei->access_type = 0;
2454       ei->access_layer = 0;
2455       ei->access_protected = 0;
2456       ei->walk_to_action = 0;
2457       ei->smash_targets = 0;
2458       ei->deadliness = 0;
2459
2460       ei->can_explode_by_fire = FALSE;
2461       ei->can_explode_smashed = FALSE;
2462       ei->can_explode_impact = FALSE;
2463
2464       ei->current_change_page = 0;
2465     }
2466
2467     if (IS_GROUP_ELEMENT(element) ||
2468         IS_INTERNAL_ELEMENT(element))
2469     {
2470       struct ElementGroupInfo *group;
2471
2472       // initialize memory for list of elements in group
2473       if (ei->group == NULL)
2474         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2475
2476       group = ei->group;
2477
2478       xx_group = *group;        // copy group data into temporary buffer
2479
2480       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2481
2482       *group = xx_group;
2483     }
2484
2485     if (IS_EMPTY_ELEMENT(element) ||
2486         IS_INTERNAL_ELEMENT(element))
2487     {
2488       xx_ei = *ei;              // copy element data into temporary buffer
2489
2490       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2491
2492       *ei = xx_ei;
2493     }
2494   }
2495
2496   clipboard_elements_initialized = TRUE;
2497 }
2498
2499 static void setLevelInfoToDefaults(struct LevelInfo *level,
2500                                    boolean level_info_only,
2501                                    boolean reset_file_status)
2502 {
2503   setLevelInfoToDefaults_Level(level);
2504
2505   if (!level_info_only)
2506     setLevelInfoToDefaults_Elements(level);
2507
2508   if (reset_file_status)
2509   {
2510     level->no_valid_file = FALSE;
2511     level->no_level_file = FALSE;
2512   }
2513
2514   level->changed = FALSE;
2515 }
2516
2517 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2518 {
2519   level_file_info->nr = 0;
2520   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2521   level_file_info->packed = FALSE;
2522
2523   setString(&level_file_info->basename, NULL);
2524   setString(&level_file_info->filename, NULL);
2525 }
2526
2527 int getMappedElement_SB(int, boolean);
2528
2529 static void ActivateLevelTemplate(void)
2530 {
2531   int x, y;
2532
2533   if (check_special_flags("load_xsb_to_ces"))
2534   {
2535     // fill smaller playfields with padding "beyond border wall" elements
2536     if (level.fieldx < level_template.fieldx ||
2537         level.fieldy < level_template.fieldy)
2538     {
2539       short field[level.fieldx][level.fieldy];
2540       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2541       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2542       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2543       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2544
2545       // copy old playfield (which is smaller than the visible area)
2546       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2547         field[x][y] = level.field[x][y];
2548
2549       // fill new, larger playfield with "beyond border wall" elements
2550       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2551         level.field[x][y] = getMappedElement_SB('_', TRUE);
2552
2553       // copy the old playfield to the middle of the new playfield
2554       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2555         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2556
2557       level.fieldx = new_fieldx;
2558       level.fieldy = new_fieldy;
2559     }
2560   }
2561
2562   // Currently there is no special action needed to activate the template
2563   // data, because 'element_info' property settings overwrite the original
2564   // level data, while all other variables do not change.
2565
2566   // Exception: 'from_level_template' elements in the original level playfield
2567   // are overwritten with the corresponding elements at the same position in
2568   // playfield from the level template.
2569
2570   for (x = 0; x < level.fieldx; x++)
2571     for (y = 0; y < level.fieldy; y++)
2572       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2573         level.field[x][y] = level_template.field[x][y];
2574
2575   if (check_special_flags("load_xsb_to_ces"))
2576   {
2577     struct LevelInfo level_backup = level;
2578
2579     // overwrite all individual level settings from template level settings
2580     level = level_template;
2581
2582     // restore level file info
2583     level.file_info = level_backup.file_info;
2584
2585     // restore playfield size
2586     level.fieldx = level_backup.fieldx;
2587     level.fieldy = level_backup.fieldy;
2588
2589     // restore playfield content
2590     for (x = 0; x < level.fieldx; x++)
2591       for (y = 0; y < level.fieldy; y++)
2592         level.field[x][y] = level_backup.field[x][y];
2593
2594     // restore name and author from individual level
2595     strcpy(level.name,   level_backup.name);
2596     strcpy(level.author, level_backup.author);
2597
2598     // restore flag "use_custom_template"
2599     level.use_custom_template = level_backup.use_custom_template;
2600   }
2601 }
2602
2603 static boolean checkForPackageFromBasename_BD(char *basename)
2604 {
2605   // check for native BD level file extensions
2606   if (!strSuffixLower(basename, ".bd") &&
2607       !strSuffixLower(basename, ".bdr") &&
2608       !strSuffixLower(basename, ".brc") &&
2609       !strSuffixLower(basename, ".gds"))
2610     return FALSE;
2611
2612   // check for standard single-level BD files (like "001.bd")
2613   if (strSuffixLower(basename, ".bd") &&
2614       strlen(basename) == 6 &&
2615       basename[0] >= '0' && basename[0] <= '9' &&
2616       basename[1] >= '0' && basename[1] <= '9' &&
2617       basename[2] >= '0' && basename[2] <= '9')
2618     return FALSE;
2619
2620   // this is a level package in native BD file format
2621   return TRUE;
2622 }
2623
2624 static char *getLevelFilenameFromBasename(char *basename)
2625 {
2626   static char *filename = NULL;
2627
2628   checked_free(filename);
2629
2630   filename = getPath2(getCurrentLevelDir(), basename);
2631
2632   return filename;
2633 }
2634
2635 static int getFileTypeFromBasename(char *basename)
2636 {
2637   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2638
2639   static char *filename = NULL;
2640   struct stat file_status;
2641
2642   // ---------- try to determine file type from filename ----------
2643
2644   // check for typical filename of a Supaplex level package file
2645   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2646     return LEVEL_FILE_TYPE_SP;
2647
2648   // check for typical filename of a Diamond Caves II level package file
2649   if (strSuffixLower(basename, ".dc") ||
2650       strSuffixLower(basename, ".dc2"))
2651     return LEVEL_FILE_TYPE_DC;
2652
2653   // check for typical filename of a Sokoban level package file
2654   if (strSuffixLower(basename, ".xsb") &&
2655       strchr(basename, '%') == NULL)
2656     return LEVEL_FILE_TYPE_SB;
2657
2658   // check for typical filename of a Boulder Dash (GDash) level package file
2659   if (checkForPackageFromBasename_BD(basename))
2660     return LEVEL_FILE_TYPE_BD;
2661
2662   // ---------- try to determine file type from filesize ----------
2663
2664   checked_free(filename);
2665   filename = getPath2(getCurrentLevelDir(), basename);
2666
2667   if (stat(filename, &file_status) == 0)
2668   {
2669     // check for typical filesize of a Supaplex level package file
2670     if (file_status.st_size == 170496)
2671       return LEVEL_FILE_TYPE_SP;
2672   }
2673
2674   return LEVEL_FILE_TYPE_UNKNOWN;
2675 }
2676
2677 static int getFileTypeFromMagicBytes(char *filename, int type)
2678 {
2679   File *file;
2680
2681   if ((file = openFile(filename, MODE_READ)))
2682   {
2683     char chunk_name[CHUNK_ID_LEN + 1];
2684
2685     getFileChunkBE(file, chunk_name, NULL);
2686
2687     if (strEqual(chunk_name, "MMII") ||
2688         strEqual(chunk_name, "MIRR"))
2689       type = LEVEL_FILE_TYPE_MM;
2690
2691     closeFile(file);
2692   }
2693
2694   return type;
2695 }
2696
2697 static boolean checkForPackageFromBasename(char *basename)
2698 {
2699   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2700   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2701
2702   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2703 }
2704
2705 static char *getSingleLevelBasenameExt(int nr, char *extension)
2706 {
2707   static char basename[MAX_FILENAME_LEN];
2708
2709   if (nr < 0)
2710     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2711   else
2712     sprintf(basename, "%03d.%s", nr, extension);
2713
2714   return basename;
2715 }
2716
2717 static char *getSingleLevelBasename(int nr)
2718 {
2719   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2720 }
2721
2722 static char *getPackedLevelBasename(int type)
2723 {
2724   static char basename[MAX_FILENAME_LEN];
2725   char *directory = getCurrentLevelDir();
2726   Directory *dir;
2727   DirectoryEntry *dir_entry;
2728
2729   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2730
2731   if ((dir = openDirectory(directory)) == NULL)
2732   {
2733     Warn("cannot read current level directory '%s'", directory);
2734
2735     return basename;
2736   }
2737
2738   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2739   {
2740     char *entry_basename = dir_entry->basename;
2741     int entry_type = getFileTypeFromBasename(entry_basename);
2742
2743     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2744     {
2745       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2746           type == entry_type)
2747       {
2748         strcpy(basename, entry_basename);
2749
2750         break;
2751       }
2752     }
2753   }
2754
2755   closeDirectory(dir);
2756
2757   return basename;
2758 }
2759
2760 static char *getSingleLevelFilename(int nr)
2761 {
2762   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2763 }
2764
2765 #if ENABLE_UNUSED_CODE
2766 static char *getPackedLevelFilename(int type)
2767 {
2768   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2769 }
2770 #endif
2771
2772 char *getDefaultLevelFilename(int nr)
2773 {
2774   return getSingleLevelFilename(nr);
2775 }
2776
2777 #if ENABLE_UNUSED_CODE
2778 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2779                                                  int type)
2780 {
2781   lfi->type = type;
2782   lfi->packed = FALSE;
2783
2784   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2785   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2786 }
2787 #endif
2788
2789 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2790                                                  int type, char *format, ...)
2791 {
2792   static char basename[MAX_FILENAME_LEN];
2793   va_list ap;
2794
2795   va_start(ap, format);
2796   vsprintf(basename, format, ap);
2797   va_end(ap);
2798
2799   lfi->type = type;
2800   lfi->packed = FALSE;
2801
2802   setString(&lfi->basename, basename);
2803   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2804 }
2805
2806 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2807                                                  int type)
2808 {
2809   lfi->type = type;
2810   lfi->packed = TRUE;
2811
2812   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2813   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2814 }
2815
2816 static int getFiletypeFromID(char *filetype_id)
2817 {
2818   char *filetype_id_lower;
2819   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2820   int i;
2821
2822   if (filetype_id == NULL)
2823     return LEVEL_FILE_TYPE_UNKNOWN;
2824
2825   filetype_id_lower = getStringToLower(filetype_id);
2826
2827   for (i = 0; filetype_id_list[i].id != NULL; i++)
2828   {
2829     char *id_lower = getStringToLower(filetype_id_list[i].id);
2830     
2831     if (strEqual(filetype_id_lower, id_lower))
2832       filetype = filetype_id_list[i].filetype;
2833
2834     free(id_lower);
2835
2836     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2837       break;
2838   }
2839
2840   free(filetype_id_lower);
2841
2842   return filetype;
2843 }
2844
2845 char *getLocalLevelTemplateFilename(void)
2846 {
2847   return getDefaultLevelFilename(-1);
2848 }
2849
2850 char *getGlobalLevelTemplateFilename(void)
2851 {
2852   // global variable "leveldir_current" must be modified in the loop below
2853   LevelDirTree *leveldir_current_last = leveldir_current;
2854   char *filename = NULL;
2855
2856   // check for template level in path from current to topmost tree node
2857
2858   while (leveldir_current != NULL)
2859   {
2860     filename = getDefaultLevelFilename(-1);
2861
2862     if (fileExists(filename))
2863       break;
2864
2865     leveldir_current = leveldir_current->node_parent;
2866   }
2867
2868   // restore global variable "leveldir_current" modified in above loop
2869   leveldir_current = leveldir_current_last;
2870
2871   return filename;
2872 }
2873
2874 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2875 {
2876   int nr = lfi->nr;
2877
2878   // special case: level number is negative => check for level template file
2879   if (nr < 0)
2880   {
2881     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2882                                          getSingleLevelBasename(-1));
2883
2884     // replace local level template filename with global template filename
2885     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2886
2887     // no fallback if template file not existing
2888     return;
2889   }
2890
2891   // special case: check for file name/pattern specified in "levelinfo.conf"
2892   if (leveldir_current->level_filename != NULL)
2893   {
2894     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2895
2896     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2897                                          leveldir_current->level_filename, nr);
2898
2899     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2900
2901     if (fileExists(lfi->filename))
2902       return;
2903   }
2904   else if (leveldir_current->level_filetype != NULL)
2905   {
2906     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2907
2908     // check for specified native level file with standard file name
2909     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2910                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2911     if (fileExists(lfi->filename))
2912       return;
2913   }
2914
2915   // check for native Rocks'n'Diamonds level file
2916   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2917                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2918   if (fileExists(lfi->filename))
2919     return;
2920
2921   // check for native Boulder Dash level file
2922   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2923   if (fileExists(lfi->filename))
2924     return;
2925
2926   // check for Emerald Mine level file (V1)
2927   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2928                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2929   if (fileExists(lfi->filename))
2930     return;
2931   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2932                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2933   if (fileExists(lfi->filename))
2934     return;
2935
2936   // check for Emerald Mine level file (V2 to V5)
2937   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2938   if (fileExists(lfi->filename))
2939     return;
2940
2941   // check for Emerald Mine level file (V6 / single mode)
2942   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2943   if (fileExists(lfi->filename))
2944     return;
2945   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2946   if (fileExists(lfi->filename))
2947     return;
2948
2949   // check for Emerald Mine level file (V6 / teamwork mode)
2950   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2951   if (fileExists(lfi->filename))
2952     return;
2953   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2954   if (fileExists(lfi->filename))
2955     return;
2956
2957   // check for various packed level file formats
2958   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2959   if (fileExists(lfi->filename))
2960     return;
2961
2962   // no known level file found -- use default values (and fail later)
2963   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2964                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2965 }
2966
2967 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2968 {
2969   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2970     lfi->type = getFileTypeFromBasename(lfi->basename);
2971
2972   if (lfi->type == LEVEL_FILE_TYPE_RND)
2973     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2974 }
2975
2976 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2977 {
2978   // always start with reliable default values
2979   setFileInfoToDefaults(level_file_info);
2980
2981   level_file_info->nr = nr;     // set requested level number
2982
2983   determineLevelFileInfo_Filename(level_file_info);
2984   determineLevelFileInfo_Filetype(level_file_info);
2985 }
2986
2987 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2988                               struct LevelFileInfo *lfi_to)
2989 {
2990   lfi_to->nr     = lfi_from->nr;
2991   lfi_to->type   = lfi_from->type;
2992   lfi_to->packed = lfi_from->packed;
2993
2994   setString(&lfi_to->basename, lfi_from->basename);
2995   setString(&lfi_to->filename, lfi_from->filename);
2996 }
2997
2998 // ----------------------------------------------------------------------------
2999 // functions for loading R'n'D level
3000 // ----------------------------------------------------------------------------
3001
3002 int getMappedElement(int element)
3003 {
3004   // remap some (historic, now obsolete) elements
3005
3006   switch (element)
3007   {
3008     case EL_PLAYER_OBSOLETE:
3009       element = EL_PLAYER_1;
3010       break;
3011
3012     case EL_KEY_OBSOLETE:
3013       element = EL_KEY_1;
3014       break;
3015
3016     case EL_EM_KEY_1_FILE_OBSOLETE:
3017       element = EL_EM_KEY_1;
3018       break;
3019
3020     case EL_EM_KEY_2_FILE_OBSOLETE:
3021       element = EL_EM_KEY_2;
3022       break;
3023
3024     case EL_EM_KEY_3_FILE_OBSOLETE:
3025       element = EL_EM_KEY_3;
3026       break;
3027
3028     case EL_EM_KEY_4_FILE_OBSOLETE:
3029       element = EL_EM_KEY_4;
3030       break;
3031
3032     case EL_ENVELOPE_OBSOLETE:
3033       element = EL_ENVELOPE_1;
3034       break;
3035
3036     case EL_SP_EMPTY:
3037       element = EL_EMPTY;
3038       break;
3039
3040     default:
3041       if (element >= NUM_FILE_ELEMENTS)
3042       {
3043         Warn("invalid level element %d", element);
3044
3045         element = EL_UNKNOWN;
3046       }
3047       break;
3048   }
3049
3050   return element;
3051 }
3052
3053 static int getMappedElementByVersion(int element, int game_version)
3054 {
3055   // remap some elements due to certain game version
3056
3057   if (game_version <= VERSION_IDENT(2,2,0,0))
3058   {
3059     // map game font elements
3060     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3061                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3062                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3063                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3064   }
3065
3066   if (game_version < VERSION_IDENT(3,0,0,0))
3067   {
3068     // map Supaplex gravity tube elements
3069     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3070                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3071                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3072                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3073                element);
3074   }
3075
3076   return element;
3077 }
3078
3079 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3080 {
3081   level->file_version = getFileVersion(file);
3082   level->game_version = getFileVersion(file);
3083
3084   return chunk_size;
3085 }
3086
3087 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3088 {
3089   level->creation_date.year  = getFile16BitBE(file);
3090   level->creation_date.month = getFile8Bit(file);
3091   level->creation_date.day   = getFile8Bit(file);
3092
3093   level->creation_date.src   = DATE_SRC_LEVELFILE;
3094
3095   return chunk_size;
3096 }
3097
3098 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3099 {
3100   int initial_player_stepsize;
3101   int initial_player_gravity;
3102   int i, x, y;
3103
3104   level->fieldx = getFile8Bit(file);
3105   level->fieldy = getFile8Bit(file);
3106
3107   level->time           = getFile16BitBE(file);
3108   level->gems_needed    = getFile16BitBE(file);
3109
3110   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3111     level->name[i] = getFile8Bit(file);
3112   level->name[MAX_LEVEL_NAME_LEN] = 0;
3113
3114   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3115     level->score[i] = getFile8Bit(file);
3116
3117   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3118   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3119     for (y = 0; y < 3; y++)
3120       for (x = 0; x < 3; x++)
3121         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3122
3123   level->amoeba_speed           = getFile8Bit(file);
3124   level->time_magic_wall        = getFile8Bit(file);
3125   level->time_wheel             = getFile8Bit(file);
3126   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3127
3128   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3129                                    STEPSIZE_NORMAL);
3130
3131   for (i = 0; i < MAX_PLAYERS; i++)
3132     level->initial_player_stepsize[i] = initial_player_stepsize;
3133
3134   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137     level->initial_player_gravity[i] = initial_player_gravity;
3138
3139   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3141
3142   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3143
3144   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3145   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3146   level->can_move_into_acid_bits = getFile32BitBE(file);
3147   level->dont_collide_with_bits = getFile8Bit(file);
3148
3149   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3150   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3151
3152   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3153   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3154   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3155
3156   level->game_engine_type       = getFile8Bit(file);
3157
3158   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3159
3160   return chunk_size;
3161 }
3162
3163 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3164 {
3165   int i;
3166
3167   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3168     level->name[i] = getFile8Bit(file);
3169   level->name[MAX_LEVEL_NAME_LEN] = 0;
3170
3171   return chunk_size;
3172 }
3173
3174 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3175 {
3176   int i;
3177
3178   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3179     level->author[i] = getFile8Bit(file);
3180   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3181
3182   return chunk_size;
3183 }
3184
3185 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3186 {
3187   int x, y;
3188   int chunk_size_expected = level->fieldx * level->fieldy;
3189
3190   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3191      stored with 16-bit encoding (and should be twice as big then).
3192      Even worse, playfield data was stored 16-bit when only yamyam content
3193      contained 16-bit elements and vice versa. */
3194
3195   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3196     chunk_size_expected *= 2;
3197
3198   if (chunk_size_expected != chunk_size)
3199   {
3200     ReadUnusedBytesFromFile(file, chunk_size);
3201     return chunk_size_expected;
3202   }
3203
3204   for (y = 0; y < level->fieldy; y++)
3205     for (x = 0; x < level->fieldx; x++)
3206       level->field[x][y] =
3207         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3208                          getFile8Bit(file));
3209   return chunk_size;
3210 }
3211
3212 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3213 {
3214   int i, x, y;
3215   int header_size = 4;
3216   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3217   int chunk_size_expected = header_size + content_size;
3218
3219   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3220      stored with 16-bit encoding (and should be twice as big then).
3221      Even worse, playfield data was stored 16-bit when only yamyam content
3222      contained 16-bit elements and vice versa. */
3223
3224   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3225     chunk_size_expected += content_size;
3226
3227   if (chunk_size_expected != chunk_size)
3228   {
3229     ReadUnusedBytesFromFile(file, chunk_size);
3230     return chunk_size_expected;
3231   }
3232
3233   getFile8Bit(file);
3234   level->num_yamyam_contents = getFile8Bit(file);
3235   getFile8Bit(file);
3236   getFile8Bit(file);
3237
3238   // correct invalid number of content fields -- should never happen
3239   if (level->num_yamyam_contents < 1 ||
3240       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3241     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3242
3243   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3244     for (y = 0; y < 3; y++)
3245       for (x = 0; x < 3; x++)
3246         level->yamyam_content[i].e[x][y] =
3247           getMappedElement(level->encoding_16bit_field ?
3248                            getFile16BitBE(file) : getFile8Bit(file));
3249   return chunk_size;
3250 }
3251
3252 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3253 {
3254   int i, x, y;
3255   int element;
3256   int num_contents;
3257   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3258
3259   element = getMappedElement(getFile16BitBE(file));
3260   num_contents = getFile8Bit(file);
3261
3262   getFile8Bit(file);    // content x size (unused)
3263   getFile8Bit(file);    // content y size (unused)
3264
3265   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3266
3267   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3268     for (y = 0; y < 3; y++)
3269       for (x = 0; x < 3; x++)
3270         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3271
3272   // correct invalid number of content fields -- should never happen
3273   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3274     num_contents = STD_ELEMENT_CONTENTS;
3275
3276   if (element == EL_YAMYAM)
3277   {
3278     level->num_yamyam_contents = num_contents;
3279
3280     for (i = 0; i < num_contents; i++)
3281       for (y = 0; y < 3; y++)
3282         for (x = 0; x < 3; x++)
3283           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3284   }
3285   else if (element == EL_BD_AMOEBA)
3286   {
3287     level->amoeba_content = content_array[0][0][0];
3288   }
3289   else
3290   {
3291     Warn("cannot load content for element '%d'", element);
3292   }
3293
3294   return chunk_size;
3295 }
3296
3297 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3298 {
3299   int i;
3300   int element;
3301   int envelope_nr;
3302   int envelope_len;
3303   int chunk_size_expected;
3304
3305   element = getMappedElement(getFile16BitBE(file));
3306   if (!IS_ENVELOPE(element))
3307     element = EL_ENVELOPE_1;
3308
3309   envelope_nr = element - EL_ENVELOPE_1;
3310
3311   envelope_len = getFile16BitBE(file);
3312
3313   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3314   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3315
3316   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3317
3318   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3319   if (chunk_size_expected != chunk_size)
3320   {
3321     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3322     return chunk_size_expected;
3323   }
3324
3325   for (i = 0; i < envelope_len; i++)
3326     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3327
3328   return chunk_size;
3329 }
3330
3331 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3332 {
3333   int num_changed_custom_elements = getFile16BitBE(file);
3334   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3335   int i;
3336
3337   if (chunk_size_expected != chunk_size)
3338   {
3339     ReadUnusedBytesFromFile(file, chunk_size - 2);
3340     return chunk_size_expected;
3341   }
3342
3343   for (i = 0; i < num_changed_custom_elements; i++)
3344   {
3345     int element = getMappedElement(getFile16BitBE(file));
3346     int properties = getFile32BitBE(file);
3347
3348     if (IS_CUSTOM_ELEMENT(element))
3349       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3350     else
3351       Warn("invalid custom element number %d", element);
3352
3353     // older game versions that wrote level files with CUS1 chunks used
3354     // different default push delay values (not yet stored in level file)
3355     element_info[element].push_delay_fixed = 2;
3356     element_info[element].push_delay_random = 8;
3357   }
3358
3359   level->file_has_custom_elements = TRUE;
3360
3361   return chunk_size;
3362 }
3363
3364 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3365 {
3366   int num_changed_custom_elements = getFile16BitBE(file);
3367   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3368   int i;
3369
3370   if (chunk_size_expected != chunk_size)
3371   {
3372     ReadUnusedBytesFromFile(file, chunk_size - 2);
3373     return chunk_size_expected;
3374   }
3375
3376   for (i = 0; i < num_changed_custom_elements; i++)
3377   {
3378     int element = getMappedElement(getFile16BitBE(file));
3379     int custom_target_element = getMappedElement(getFile16BitBE(file));
3380
3381     if (IS_CUSTOM_ELEMENT(element))
3382       element_info[element].change->target_element = custom_target_element;
3383     else
3384       Warn("invalid custom element number %d", element);
3385   }
3386
3387   level->file_has_custom_elements = TRUE;
3388
3389   return chunk_size;
3390 }
3391
3392 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3393 {
3394   int num_changed_custom_elements = getFile16BitBE(file);
3395   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3396   int i, j, x, y;
3397
3398   if (chunk_size_expected != chunk_size)
3399   {
3400     ReadUnusedBytesFromFile(file, chunk_size - 2);
3401     return chunk_size_expected;
3402   }
3403
3404   for (i = 0; i < num_changed_custom_elements; i++)
3405   {
3406     int element = getMappedElement(getFile16BitBE(file));
3407     struct ElementInfo *ei = &element_info[element];
3408     unsigned int event_bits;
3409
3410     if (!IS_CUSTOM_ELEMENT(element))
3411     {
3412       Warn("invalid custom element number %d", element);
3413
3414       element = EL_INTERNAL_DUMMY;
3415     }
3416
3417     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3418       ei->description[j] = getFile8Bit(file);
3419     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3420
3421     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3422
3423     // some free bytes for future properties and padding
3424     ReadUnusedBytesFromFile(file, 7);
3425
3426     ei->use_gfx_element = getFile8Bit(file);
3427     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3428
3429     ei->collect_score_initial = getFile8Bit(file);
3430     ei->collect_count_initial = getFile8Bit(file);
3431
3432     ei->push_delay_fixed = getFile16BitBE(file);
3433     ei->push_delay_random = getFile16BitBE(file);
3434     ei->move_delay_fixed = getFile16BitBE(file);
3435     ei->move_delay_random = getFile16BitBE(file);
3436
3437     ei->move_pattern = getFile16BitBE(file);
3438     ei->move_direction_initial = getFile8Bit(file);
3439     ei->move_stepsize = getFile8Bit(file);
3440
3441     for (y = 0; y < 3; y++)
3442       for (x = 0; x < 3; x++)
3443         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3444
3445     // bits 0 - 31 of "has_event[]"
3446     event_bits = getFile32BitBE(file);
3447     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3448       if (event_bits & (1u << j))
3449         ei->change->has_event[j] = TRUE;
3450
3451     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3452
3453     ei->change->delay_fixed = getFile16BitBE(file);
3454     ei->change->delay_random = getFile16BitBE(file);
3455     ei->change->delay_frames = getFile16BitBE(file);
3456
3457     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3458
3459     ei->change->explode = getFile8Bit(file);
3460     ei->change->use_target_content = getFile8Bit(file);
3461     ei->change->only_if_complete = getFile8Bit(file);
3462     ei->change->use_random_replace = getFile8Bit(file);
3463
3464     ei->change->random_percentage = getFile8Bit(file);
3465     ei->change->replace_when = getFile8Bit(file);
3466
3467     for (y = 0; y < 3; y++)
3468       for (x = 0; x < 3; x++)
3469         ei->change->target_content.e[x][y] =
3470           getMappedElement(getFile16BitBE(file));
3471
3472     ei->slippery_type = getFile8Bit(file);
3473
3474     // some free bytes for future properties and padding
3475     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3476
3477     // mark that this custom element has been modified
3478     ei->modified_settings = TRUE;
3479   }
3480
3481   level->file_has_custom_elements = TRUE;
3482
3483   return chunk_size;
3484 }
3485
3486 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3487 {
3488   struct ElementInfo *ei;
3489   int chunk_size_expected;
3490   int element;
3491   int i, j, x, y;
3492
3493   // ---------- custom element base property values (96 bytes) ----------------
3494
3495   element = getMappedElement(getFile16BitBE(file));
3496
3497   if (!IS_CUSTOM_ELEMENT(element))
3498   {
3499     Warn("invalid custom element number %d", element);
3500
3501     ReadUnusedBytesFromFile(file, chunk_size - 2);
3502
3503     return chunk_size;
3504   }
3505
3506   ei = &element_info[element];
3507
3508   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3509     ei->description[i] = getFile8Bit(file);
3510   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3511
3512   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3513
3514   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3515
3516   ei->num_change_pages = getFile8Bit(file);
3517
3518   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3519   if (chunk_size_expected != chunk_size)
3520   {
3521     ReadUnusedBytesFromFile(file, chunk_size - 43);
3522     return chunk_size_expected;
3523   }
3524
3525   ei->ce_value_fixed_initial = getFile16BitBE(file);
3526   ei->ce_value_random_initial = getFile16BitBE(file);
3527   ei->use_last_ce_value = getFile8Bit(file);
3528
3529   ei->use_gfx_element = getFile8Bit(file);
3530   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3531
3532   ei->collect_score_initial = getFile8Bit(file);
3533   ei->collect_count_initial = getFile8Bit(file);
3534
3535   ei->drop_delay_fixed = getFile8Bit(file);
3536   ei->push_delay_fixed = getFile8Bit(file);
3537   ei->drop_delay_random = getFile8Bit(file);
3538   ei->push_delay_random = getFile8Bit(file);
3539   ei->move_delay_fixed = getFile16BitBE(file);
3540   ei->move_delay_random = getFile16BitBE(file);
3541
3542   // bits 0 - 15 of "move_pattern" ...
3543   ei->move_pattern = getFile16BitBE(file);
3544   ei->move_direction_initial = getFile8Bit(file);
3545   ei->move_stepsize = getFile8Bit(file);
3546
3547   ei->slippery_type = getFile8Bit(file);
3548
3549   for (y = 0; y < 3; y++)
3550     for (x = 0; x < 3; x++)
3551       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3552
3553   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3554   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3555   ei->move_leave_type = getFile8Bit(file);
3556
3557   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3558   ei->move_pattern |= (getFile16BitBE(file) << 16);
3559
3560   ei->access_direction = getFile8Bit(file);
3561
3562   ei->explosion_delay = getFile8Bit(file);
3563   ei->ignition_delay = getFile8Bit(file);
3564   ei->explosion_type = getFile8Bit(file);
3565
3566   // some free bytes for future custom property values and padding
3567   ReadUnusedBytesFromFile(file, 1);
3568
3569   // ---------- change page property values (48 bytes) ------------------------
3570
3571   setElementChangePages(ei, ei->num_change_pages);
3572
3573   for (i = 0; i < ei->num_change_pages; i++)
3574   {
3575     struct ElementChangeInfo *change = &ei->change_page[i];
3576     unsigned int event_bits;
3577
3578     // always start with reliable default values
3579     setElementChangeInfoToDefaults(change);
3580
3581     // bits 0 - 31 of "has_event[]" ...
3582     event_bits = getFile32BitBE(file);
3583     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3584       if (event_bits & (1u << j))
3585         change->has_event[j] = TRUE;
3586
3587     change->target_element = getMappedElement(getFile16BitBE(file));
3588
3589     change->delay_fixed = getFile16BitBE(file);
3590     change->delay_random = getFile16BitBE(file);
3591     change->delay_frames = getFile16BitBE(file);
3592
3593     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3594
3595     change->explode = getFile8Bit(file);
3596     change->use_target_content = getFile8Bit(file);
3597     change->only_if_complete = getFile8Bit(file);
3598     change->use_random_replace = getFile8Bit(file);
3599
3600     change->random_percentage = getFile8Bit(file);
3601     change->replace_when = getFile8Bit(file);
3602
3603     for (y = 0; y < 3; y++)
3604       for (x = 0; x < 3; x++)
3605         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3606
3607     change->can_change = getFile8Bit(file);
3608
3609     change->trigger_side = getFile8Bit(file);
3610
3611     change->trigger_player = getFile8Bit(file);
3612     change->trigger_page = getFile8Bit(file);
3613
3614     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3615                             CH_PAGE_ANY : (1 << change->trigger_page));
3616
3617     change->has_action = getFile8Bit(file);
3618     change->action_type = getFile8Bit(file);
3619     change->action_mode = getFile8Bit(file);
3620     change->action_arg = getFile16BitBE(file);
3621
3622     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3623     event_bits = getFile8Bit(file);
3624     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3625       if (event_bits & (1u << (j - 32)))
3626         change->has_event[j] = TRUE;
3627   }
3628
3629   // mark this custom element as modified
3630   ei->modified_settings = TRUE;
3631
3632   level->file_has_custom_elements = TRUE;
3633
3634   return chunk_size;
3635 }
3636
3637 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3638 {
3639   struct ElementInfo *ei;
3640   struct ElementGroupInfo *group;
3641   int element;
3642   int i;
3643
3644   element = getMappedElement(getFile16BitBE(file));
3645
3646   if (!IS_GROUP_ELEMENT(element))
3647   {
3648     Warn("invalid group element number %d", element);
3649
3650     ReadUnusedBytesFromFile(file, chunk_size - 2);
3651
3652     return chunk_size;
3653   }
3654
3655   ei = &element_info[element];
3656
3657   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3658     ei->description[i] = getFile8Bit(file);
3659   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3660
3661   group = element_info[element].group;
3662
3663   group->num_elements = getFile8Bit(file);
3664
3665   ei->use_gfx_element = getFile8Bit(file);
3666   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3667
3668   group->choice_mode = getFile8Bit(file);
3669
3670   // some free bytes for future values and padding
3671   ReadUnusedBytesFromFile(file, 3);
3672
3673   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3674     group->element[i] = getMappedElement(getFile16BitBE(file));
3675
3676   // mark this group element as modified
3677   element_info[element].modified_settings = TRUE;
3678
3679   level->file_has_custom_elements = TRUE;
3680
3681   return chunk_size;
3682 }
3683
3684 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3685                                 int element, int real_element)
3686 {
3687   int micro_chunk_size = 0;
3688   int conf_type = getFile8Bit(file);
3689   int byte_mask = conf_type & CONF_MASK_BYTES;
3690   boolean element_found = FALSE;
3691   int i;
3692
3693   micro_chunk_size += 1;
3694
3695   if (byte_mask == CONF_MASK_MULTI_BYTES)
3696   {
3697     int num_bytes = getFile16BitBE(file);
3698     byte *buffer = checked_malloc(num_bytes);
3699
3700     ReadBytesFromFile(file, buffer, num_bytes);
3701
3702     for (i = 0; conf[i].data_type != -1; i++)
3703     {
3704       if (conf[i].element == element &&
3705           conf[i].conf_type == conf_type)
3706       {
3707         int data_type = conf[i].data_type;
3708         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3709         int max_num_entities = conf[i].max_num_entities;
3710
3711         if (num_entities > max_num_entities)
3712         {
3713           Warn("truncating number of entities for element %d from %d to %d",
3714                element, num_entities, max_num_entities);
3715
3716           num_entities = max_num_entities;
3717         }
3718
3719         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3720                                   data_type == TYPE_CONTENT_LIST))
3721         {
3722           // for element and content lists, zero entities are not allowed
3723           Warn("found empty list of entities for element %d", element);
3724
3725           // do not set "num_entities" here to prevent reading behind buffer
3726
3727           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3728         }
3729         else
3730         {
3731           *(int *)(conf[i].num_entities) = num_entities;
3732         }
3733
3734         element_found = TRUE;
3735
3736         if (data_type == TYPE_STRING)
3737         {
3738           char *string = (char *)(conf[i].value);
3739           int j;
3740
3741           for (j = 0; j < max_num_entities; j++)
3742             string[j] = (j < num_entities ? buffer[j] : '\0');
3743         }
3744         else if (data_type == TYPE_ELEMENT_LIST)
3745         {
3746           int *element_array = (int *)(conf[i].value);
3747           int j;
3748
3749           for (j = 0; j < num_entities; j++)
3750             element_array[j] =
3751               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3752         }
3753         else if (data_type == TYPE_CONTENT_LIST)
3754         {
3755           struct Content *content= (struct Content *)(conf[i].value);
3756           int c, x, y;
3757
3758           for (c = 0; c < num_entities; c++)
3759             for (y = 0; y < 3; y++)
3760               for (x = 0; x < 3; x++)
3761                 content[c].e[x][y] =
3762                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3763         }
3764         else
3765           element_found = FALSE;
3766
3767         break;
3768       }
3769     }
3770
3771     checked_free(buffer);
3772
3773     micro_chunk_size += 2 + num_bytes;
3774   }
3775   else          // constant size configuration data (1, 2 or 4 bytes)
3776   {
3777     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3778                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3779                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3780
3781     for (i = 0; conf[i].data_type != -1; i++)
3782     {
3783       if (conf[i].element == element &&
3784           conf[i].conf_type == conf_type)
3785       {
3786         int data_type = conf[i].data_type;
3787
3788         if (data_type == TYPE_ELEMENT)
3789           value = getMappedElement(value);
3790
3791         if (data_type == TYPE_BOOLEAN)
3792           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3793         else
3794           *(int *)    (conf[i].value) = value;
3795
3796         element_found = TRUE;
3797
3798         break;
3799       }
3800     }
3801
3802     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3803   }
3804
3805   if (!element_found)
3806   {
3807     char *error_conf_chunk_bytes =
3808       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3809        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3810        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3811     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3812     int error_element = real_element;
3813
3814     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3815          error_conf_chunk_bytes, error_conf_chunk_token,
3816          error_element, EL_NAME(error_element));
3817   }
3818
3819   return micro_chunk_size;
3820 }
3821
3822 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3823 {
3824   int real_chunk_size = 0;
3825
3826   li = *level;          // copy level data into temporary buffer
3827
3828   while (!checkEndOfFile(file))
3829   {
3830     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3831
3832     if (real_chunk_size >= chunk_size)
3833       break;
3834   }
3835
3836   *level = li;          // copy temporary buffer back to level data
3837
3838   return real_chunk_size;
3839 }
3840
3841 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3842 {
3843   int real_chunk_size = 0;
3844
3845   li = *level;          // copy level data into temporary buffer
3846
3847   while (!checkEndOfFile(file))
3848   {
3849     int element = getMappedElement(getFile16BitBE(file));
3850
3851     real_chunk_size += 2;
3852     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3853                                             element, element);
3854     if (real_chunk_size >= chunk_size)
3855       break;
3856   }
3857
3858   *level = li;          // copy temporary buffer back to level data
3859
3860   return real_chunk_size;
3861 }
3862
3863 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3864 {
3865   int real_chunk_size = 0;
3866
3867   li = *level;          // copy level data into temporary buffer
3868
3869   while (!checkEndOfFile(file))
3870   {
3871     int element = getMappedElement(getFile16BitBE(file));
3872
3873     real_chunk_size += 2;
3874     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3875                                             element, element);
3876     if (real_chunk_size >= chunk_size)
3877       break;
3878   }
3879
3880   *level = li;          // copy temporary buffer back to level data
3881
3882   return real_chunk_size;
3883 }
3884
3885 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3886 {
3887   int element = getMappedElement(getFile16BitBE(file));
3888   int envelope_nr = element - EL_ENVELOPE_1;
3889   int real_chunk_size = 2;
3890
3891   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3892
3893   while (!checkEndOfFile(file))
3894   {
3895     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3896                                             -1, element);
3897
3898     if (real_chunk_size >= chunk_size)
3899       break;
3900   }
3901
3902   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3903
3904   return real_chunk_size;
3905 }
3906
3907 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3908 {
3909   int element = getMappedElement(getFile16BitBE(file));
3910   int real_chunk_size = 2;
3911   struct ElementInfo *ei = &element_info[element];
3912   int i;
3913
3914   xx_ei = *ei;          // copy element data into temporary buffer
3915
3916   xx_ei.num_change_pages = -1;
3917
3918   while (!checkEndOfFile(file))
3919   {
3920     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3921                                             -1, element);
3922     if (xx_ei.num_change_pages != -1)
3923       break;
3924
3925     if (real_chunk_size >= chunk_size)
3926       break;
3927   }
3928
3929   *ei = xx_ei;
3930
3931   if (ei->num_change_pages == -1)
3932   {
3933     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3934          EL_NAME(element));
3935
3936     ei->num_change_pages = 1;
3937
3938     setElementChangePages(ei, 1);
3939     setElementChangeInfoToDefaults(ei->change);
3940
3941     return real_chunk_size;
3942   }
3943
3944   // initialize number of change pages stored for this custom element
3945   setElementChangePages(ei, ei->num_change_pages);
3946   for (i = 0; i < ei->num_change_pages; i++)
3947     setElementChangeInfoToDefaults(&ei->change_page[i]);
3948
3949   // start with reading properties for the first change page
3950   xx_current_change_page = 0;
3951
3952   while (!checkEndOfFile(file))
3953   {
3954     // level file might contain invalid change page number
3955     if (xx_current_change_page >= ei->num_change_pages)
3956       break;
3957
3958     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3959
3960     xx_change = *change;        // copy change data into temporary buffer
3961
3962     resetEventBits();           // reset bits; change page might have changed
3963
3964     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3965                                             -1, element);
3966
3967     *change = xx_change;
3968
3969     setEventFlagsFromEventBits(change);
3970
3971     if (real_chunk_size >= chunk_size)
3972       break;
3973   }
3974
3975   level->file_has_custom_elements = TRUE;
3976
3977   return real_chunk_size;
3978 }
3979
3980 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3981 {
3982   int element = getMappedElement(getFile16BitBE(file));
3983   int real_chunk_size = 2;
3984   struct ElementInfo *ei = &element_info[element];
3985   struct ElementGroupInfo *group = ei->group;
3986
3987   if (group == NULL)
3988     return -1;
3989
3990   xx_ei = *ei;          // copy element data into temporary buffer
3991   xx_group = *group;    // copy group data into temporary buffer
3992
3993   while (!checkEndOfFile(file))
3994   {
3995     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3996                                             -1, element);
3997
3998     if (real_chunk_size >= chunk_size)
3999       break;
4000   }
4001
4002   *ei = xx_ei;
4003   *group = xx_group;
4004
4005   level->file_has_custom_elements = TRUE;
4006
4007   return real_chunk_size;
4008 }
4009
4010 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4011 {
4012   int element = getMappedElement(getFile16BitBE(file));
4013   int real_chunk_size = 2;
4014   struct ElementInfo *ei = &element_info[element];
4015
4016   xx_ei = *ei;          // copy element data into temporary buffer
4017
4018   while (!checkEndOfFile(file))
4019   {
4020     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4021                                             -1, element);
4022
4023     if (real_chunk_size >= chunk_size)
4024       break;
4025   }
4026
4027   *ei = xx_ei;
4028
4029   level->file_has_custom_elements = TRUE;
4030
4031   return real_chunk_size;
4032 }
4033
4034 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4035                                       struct LevelFileInfo *level_file_info,
4036                                       boolean level_info_only)
4037 {
4038   char *filename = level_file_info->filename;
4039   char cookie[MAX_LINE_LEN];
4040   char chunk_name[CHUNK_ID_LEN + 1];
4041   int chunk_size;
4042   File *file;
4043
4044   if (!(file = openFile(filename, MODE_READ)))
4045   {
4046     level->no_valid_file = TRUE;
4047     level->no_level_file = TRUE;
4048
4049     if (level_info_only)
4050       return;
4051
4052     Warn("cannot read level '%s' -- using empty level", filename);
4053
4054     if (!setup.editor.use_template_for_new_levels)
4055       return;
4056
4057     // if level file not found, try to initialize level data from template
4058     filename = getGlobalLevelTemplateFilename();
4059
4060     if (!(file = openFile(filename, MODE_READ)))
4061       return;
4062
4063     // default: for empty levels, use level template for custom elements
4064     level->use_custom_template = TRUE;
4065
4066     level->no_valid_file = FALSE;
4067   }
4068
4069   getFileChunkBE(file, chunk_name, NULL);
4070   if (strEqual(chunk_name, "RND1"))
4071   {
4072     getFile32BitBE(file);               // not used
4073
4074     getFileChunkBE(file, chunk_name, NULL);
4075     if (!strEqual(chunk_name, "CAVE"))
4076     {
4077       level->no_valid_file = TRUE;
4078
4079       Warn("unknown format of level file '%s'", filename);
4080
4081       closeFile(file);
4082
4083       return;
4084     }
4085   }
4086   else  // check for pre-2.0 file format with cookie string
4087   {
4088     strcpy(cookie, chunk_name);
4089     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4090       cookie[4] = '\0';
4091     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4092       cookie[strlen(cookie) - 1] = '\0';
4093
4094     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4095     {
4096       level->no_valid_file = TRUE;
4097
4098       Warn("unknown format of level file '%s'", filename);
4099
4100       closeFile(file);
4101
4102       return;
4103     }
4104
4105     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4106     {
4107       level->no_valid_file = TRUE;
4108
4109       Warn("unsupported version of level file '%s'", filename);
4110
4111       closeFile(file);
4112
4113       return;
4114     }
4115
4116     // pre-2.0 level files have no game version, so use file version here
4117     level->game_version = level->file_version;
4118   }
4119
4120   if (level->file_version < FILE_VERSION_1_2)
4121   {
4122     // level files from versions before 1.2.0 without chunk structure
4123     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4124     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4125   }
4126   else
4127   {
4128     static struct
4129     {
4130       char *name;
4131       int size;
4132       int (*loader)(File *, int, struct LevelInfo *);
4133     }
4134     chunk_info[] =
4135     {
4136       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4137       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4138       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4139       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4140       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4141       { "INFO", -1,                     LoadLevel_INFO },
4142       { "BODY", -1,                     LoadLevel_BODY },
4143       { "CONT", -1,                     LoadLevel_CONT },
4144       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4145       { "CNT3", -1,                     LoadLevel_CNT3 },
4146       { "CUS1", -1,                     LoadLevel_CUS1 },
4147       { "CUS2", -1,                     LoadLevel_CUS2 },
4148       { "CUS3", -1,                     LoadLevel_CUS3 },
4149       { "CUS4", -1,                     LoadLevel_CUS4 },
4150       { "GRP1", -1,                     LoadLevel_GRP1 },
4151       { "CONF", -1,                     LoadLevel_CONF },
4152       { "ELEM", -1,                     LoadLevel_ELEM },
4153       { "NOTE", -1,                     LoadLevel_NOTE },
4154       { "CUSX", -1,                     LoadLevel_CUSX },
4155       { "GRPX", -1,                     LoadLevel_GRPX },
4156       { "EMPX", -1,                     LoadLevel_EMPX },
4157
4158       {  NULL,  0,                      NULL }
4159     };
4160
4161     while (getFileChunkBE(file, chunk_name, &chunk_size))
4162     {
4163       int i = 0;
4164
4165       while (chunk_info[i].name != NULL &&
4166              !strEqual(chunk_name, chunk_info[i].name))
4167         i++;
4168
4169       if (chunk_info[i].name == NULL)
4170       {
4171         Warn("unknown chunk '%s' in level file '%s'",
4172              chunk_name, filename);
4173
4174         ReadUnusedBytesFromFile(file, chunk_size);
4175       }
4176       else if (chunk_info[i].size != -1 &&
4177                chunk_info[i].size != chunk_size)
4178       {
4179         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4180              chunk_size, chunk_name, filename);
4181
4182         ReadUnusedBytesFromFile(file, chunk_size);
4183       }
4184       else
4185       {
4186         // call function to load this level chunk
4187         int chunk_size_expected =
4188           (chunk_info[i].loader)(file, chunk_size, level);
4189
4190         if (chunk_size_expected < 0)
4191         {
4192           Warn("error reading chunk '%s' in level file '%s'",
4193                chunk_name, filename);
4194
4195           break;
4196         }
4197
4198         // the size of some chunks cannot be checked before reading other
4199         // chunks first (like "HEAD" and "BODY") that contain some header
4200         // information, so check them here
4201         if (chunk_size_expected != chunk_size)
4202         {
4203           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4204                chunk_size, chunk_name, filename);
4205
4206           break;
4207         }
4208       }
4209     }
4210   }
4211
4212   closeFile(file);
4213 }
4214
4215
4216 // ----------------------------------------------------------------------------
4217 // functions for loading BD level
4218 // ----------------------------------------------------------------------------
4219
4220 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4221 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4222
4223 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4224 {
4225   struct LevelInfo_BD *level_bd = level->native_bd_level;
4226   GdCave *cave = NULL;  // will be changed below
4227   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4228   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4229   int x, y;
4230
4231   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4232
4233   // cave and map newly allocated when set to defaults above
4234   cave = level_bd->cave;
4235
4236   // level type
4237   cave->intermission                    = level->bd_intermission;
4238
4239   // level settings
4240   cave->level_time[0]                   = level->time;
4241   cave->level_diamonds[0]               = level->gems_needed;
4242
4243   // game timing
4244   cave->scheduling                      = level->bd_scheduling_type;
4245   cave->pal_timing                      = level->bd_pal_timing;
4246   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4247   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4248   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4249   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4250
4251   // scores
4252   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4253   cave->diamond_value                   = level->score[SC_EMERALD];
4254   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4255
4256   // compatibility settings
4257   cave->lineshift                       = level->bd_line_shifting_borders;
4258   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4259   cave->short_explosions                = level->bd_short_explosions;
4260
4261   // player properties
4262   cave->diagonal_movements              = level->bd_diagonal_movements;
4263   cave->active_is_first_found           = level->bd_topmost_player_active;
4264   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4265   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4266   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4267   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4268
4269   // element properties
4270   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4271   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4272   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4273   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4274   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4275   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4276   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4277   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4278   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4279   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4280   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4281
4282   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4283   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4284   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4285   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4286   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4287   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4288   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4289
4290   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4291   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4292   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4293   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4294   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4295   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4296   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4297   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4298   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4299   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4300   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4301
4302   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4303   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4304   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4305   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4306   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4307   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4308
4309   cave->slime_predictable               = level->bd_slime_is_predictable;
4310   cave->slime_correct_random            = level->bd_slime_correct_random;
4311   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4312   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4313   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4314   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4315   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4316   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4317   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4318   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4319   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4320   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4321
4322   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4323   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4324   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4325
4326   cave->biter_delay_frame               = level->bd_biter_move_delay;
4327   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4328
4329   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4330
4331   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4332
4333   cave->replicators_active              = level->bd_replicators_active;
4334   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4335
4336   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4337   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4338
4339   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4340
4341   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4342
4343   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4344   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4345   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4346
4347   cave->infinite_rockets                = level->bd_infinite_rockets;
4348
4349   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4350   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4351
4352   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4353   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4354
4355   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4356   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4357   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4358
4359   cave->gravity                         = level->bd_gravity_direction;
4360   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4361   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4362   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4363
4364   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4365   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4366   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4367   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4368
4369   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4370   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4371   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4372   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4373   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4374   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4375
4376   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4377   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4378   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4379   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4380
4381   // level name
4382   strncpy(cave->name, level->name, sizeof(GdString));
4383   cave->name[sizeof(GdString) - 1] = '\0';
4384
4385   // playfield elements
4386   for (x = 0; x < cave->w; x++)
4387     for (y = 0; y < cave->h; y++)
4388       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4389 }
4390
4391 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4392 {
4393   struct LevelInfo_BD *level_bd = level->native_bd_level;
4394   GdCave *cave = level_bd->cave;
4395   int bd_level_nr = level_bd->level_nr;
4396   int x, y;
4397
4398   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4399   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4400
4401   // level type
4402   level->bd_intermission                = cave->intermission;
4403
4404   // level settings
4405   level->time                           = cave->level_time[bd_level_nr];
4406   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4407
4408   // game timing
4409   level->bd_scheduling_type             = cave->scheduling;
4410   level->bd_pal_timing                  = cave->pal_timing;
4411   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4412   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4413   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4414   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4415
4416   // scores
4417   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4418   level->score[SC_EMERALD]              = cave->diamond_value;
4419   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4420
4421   // compatibility settings
4422   level->bd_line_shifting_borders       = cave->lineshift;
4423   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4424   level->bd_short_explosions            = cave->short_explosions;
4425
4426   // player properties
4427   level->bd_diagonal_movements          = cave->diagonal_movements;
4428   level->bd_topmost_player_active       = cave->active_is_first_found;
4429   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4430   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4431   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4432   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4433
4434   // element properties
4435   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4436   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4437   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4438   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4439   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4440   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4441   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4442   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4443   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4444   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4445   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4446
4447   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4448   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4449   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4450   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4451   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4452   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4453   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4454
4455   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4456   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4457   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4458   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4459   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4460   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4461   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4462   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4463   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4464   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4465   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4466
4467   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4468   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4469   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4470   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4471   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4472   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4473
4474   level->bd_slime_is_predictable        = cave->slime_predictable;
4475   level->bd_slime_correct_random        = cave->slime_correct_random;
4476   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4477   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4478   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4479   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4480   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4481   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4482   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4483   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4484   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4485   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4486
4487   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4488   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4489   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4490
4491   level->bd_biter_move_delay            = cave->biter_delay_frame;
4492   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4493
4494   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4495
4496   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4497
4498   level->bd_replicators_active          = cave->replicators_active;
4499   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4500
4501   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4502   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4503
4504   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4505
4506   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4507
4508   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4509   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4510   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4511
4512   level->bd_infinite_rockets            = cave->infinite_rockets;
4513
4514   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4515   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4516
4517   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4518   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4519
4520   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4521   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4522   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4523
4524   level->bd_gravity_direction           = cave->gravity;
4525   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4526   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4527   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4528
4529   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4530   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4531   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4532   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4533
4534   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4535   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4536   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4537   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4538   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4539   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4540
4541   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4542   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4543   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4544   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4545
4546   // level name
4547   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4548
4549   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4550   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4551
4552   // playfield elements
4553   for (x = 0; x < level->fieldx; x++)
4554     for (y = 0; y < level->fieldy; y++)
4555       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4556
4557   checked_free(cave_name);
4558 }
4559
4560 static void setTapeInfoToDefaults(void);
4561
4562 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4563 {
4564   struct LevelInfo_BD *level_bd = level->native_bd_level;
4565   GdCave *cave = level_bd->cave;
4566   GdReplay *replay = level_bd->replay;
4567   int i;
4568
4569   if (replay == NULL)
4570     return;
4571
4572   // always start with reliable default values
4573   setTapeInfoToDefaults();
4574
4575   tape.level_nr = level_nr;             // (currently not used)
4576   tape.random_seed = replay->seed;
4577
4578   TapeSetDateFromIsoDateString(replay->date);
4579
4580   tape.counter = 0;
4581   tape.pos[tape.counter].delay = 0;
4582
4583   tape.bd_replay = TRUE;
4584
4585   // all time calculations only used to display approximate tape time
4586   int cave_speed = cave->speed;
4587   int milliseconds_game = 0;
4588   int milliseconds_elapsed = 20;
4589
4590   for (i = 0; i < replay->movements->len; i++)
4591   {
4592     int replay_action = replay->movements->data[i];
4593     int tape_action = map_action_BD_to_RND(replay_action);
4594     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4595     boolean success = 0;
4596
4597     while (1)
4598     {
4599       success = TapeAddAction(action);
4600
4601       milliseconds_game += milliseconds_elapsed;
4602
4603       if (milliseconds_game >= cave_speed)
4604       {
4605         milliseconds_game -= cave_speed;
4606
4607         break;
4608       }
4609     }
4610
4611     tape.counter++;
4612     tape.pos[tape.counter].delay = 0;
4613     tape.pos[tape.counter].action[0] = 0;
4614
4615     if (!success)
4616     {
4617       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4618
4619       break;
4620     }
4621   }
4622
4623   TapeHaltRecording();
4624
4625   if (!replay->success)
4626     Warn("BD replay is marked as not successful");
4627 }
4628
4629
4630 // ----------------------------------------------------------------------------
4631 // functions for loading EM level
4632 // ----------------------------------------------------------------------------
4633
4634 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4635 {
4636   static int ball_xy[8][2] =
4637   {
4638     { 0, 0 },
4639     { 1, 0 },
4640     { 2, 0 },
4641     { 0, 1 },
4642     { 2, 1 },
4643     { 0, 2 },
4644     { 1, 2 },
4645     { 2, 2 },
4646   };
4647   struct LevelInfo_EM *level_em = level->native_em_level;
4648   struct CAVE *cav = level_em->cav;
4649   int i, j, x, y;
4650
4651   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4652   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4653
4654   cav->time_seconds     = level->time;
4655   cav->gems_needed      = level->gems_needed;
4656
4657   cav->emerald_score    = level->score[SC_EMERALD];
4658   cav->diamond_score    = level->score[SC_DIAMOND];
4659   cav->alien_score      = level->score[SC_ROBOT];
4660   cav->tank_score       = level->score[SC_SPACESHIP];
4661   cav->bug_score        = level->score[SC_BUG];
4662   cav->eater_score      = level->score[SC_YAMYAM];
4663   cav->nut_score        = level->score[SC_NUT];
4664   cav->dynamite_score   = level->score[SC_DYNAMITE];
4665   cav->key_score        = level->score[SC_KEY];
4666   cav->exit_score       = level->score[SC_TIME_BONUS];
4667
4668   cav->num_eater_arrays = level->num_yamyam_contents;
4669
4670   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4671     for (y = 0; y < 3; y++)
4672       for (x = 0; x < 3; x++)
4673         cav->eater_array[i][y * 3 + x] =
4674           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4675
4676   cav->amoeba_time              = level->amoeba_speed;
4677   cav->wonderwall_time          = level->time_magic_wall;
4678   cav->wheel_time               = level->time_wheel;
4679
4680   cav->android_move_time        = level->android_move_time;
4681   cav->android_clone_time       = level->android_clone_time;
4682   cav->ball_random              = level->ball_random;
4683   cav->ball_active              = level->ball_active_initial;
4684   cav->ball_time                = level->ball_time;
4685   cav->num_ball_arrays          = level->num_ball_contents;
4686
4687   cav->lenses_score             = level->lenses_score;
4688   cav->magnify_score            = level->magnify_score;
4689   cav->slurp_score              = level->slurp_score;
4690
4691   cav->lenses_time              = level->lenses_time;
4692   cav->magnify_time             = level->magnify_time;
4693
4694   cav->wind_time = 9999;
4695   cav->wind_direction =
4696     map_direction_RND_to_EM(level->wind_direction_initial);
4697
4698   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4699     for (j = 0; j < 8; j++)
4700       cav->ball_array[i][j] =
4701         map_element_RND_to_EM_cave(level->ball_content[i].
4702                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4703
4704   map_android_clone_elements_RND_to_EM(level);
4705
4706   // first fill the complete playfield with the empty space element
4707   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4708     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4709       cav->cave[x][y] = Cblank;
4710
4711   // then copy the real level contents from level file into the playfield
4712   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4713   {
4714     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4715
4716     if (level->field[x][y] == EL_AMOEBA_DEAD)
4717       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4718
4719     cav->cave[x][y] = new_element;
4720   }
4721
4722   for (i = 0; i < MAX_PLAYERS; i++)
4723   {
4724     cav->player_x[i] = -1;
4725     cav->player_y[i] = -1;
4726   }
4727
4728   // initialize player positions and delete players from the playfield
4729   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4730   {
4731     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4732     {
4733       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4734
4735       cav->player_x[player_nr] = x;
4736       cav->player_y[player_nr] = y;
4737
4738       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4739     }
4740   }
4741 }
4742
4743 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4744 {
4745   static int ball_xy[8][2] =
4746   {
4747     { 0, 0 },
4748     { 1, 0 },
4749     { 2, 0 },
4750     { 0, 1 },
4751     { 2, 1 },
4752     { 0, 2 },
4753     { 1, 2 },
4754     { 2, 2 },
4755   };
4756   struct LevelInfo_EM *level_em = level->native_em_level;
4757   struct CAVE *cav = level_em->cav;
4758   int i, j, x, y;
4759
4760   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4761   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4762
4763   level->time        = cav->time_seconds;
4764   level->gems_needed = cav->gems_needed;
4765
4766   sprintf(level->name, "Level %d", level->file_info.nr);
4767
4768   level->score[SC_EMERALD]      = cav->emerald_score;
4769   level->score[SC_DIAMOND]      = cav->diamond_score;
4770   level->score[SC_ROBOT]        = cav->alien_score;
4771   level->score[SC_SPACESHIP]    = cav->tank_score;
4772   level->score[SC_BUG]          = cav->bug_score;
4773   level->score[SC_YAMYAM]       = cav->eater_score;
4774   level->score[SC_NUT]          = cav->nut_score;
4775   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4776   level->score[SC_KEY]          = cav->key_score;
4777   level->score[SC_TIME_BONUS]   = cav->exit_score;
4778
4779   level->num_yamyam_contents    = cav->num_eater_arrays;
4780
4781   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4782     for (y = 0; y < 3; y++)
4783       for (x = 0; x < 3; x++)
4784         level->yamyam_content[i].e[x][y] =
4785           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4786
4787   level->amoeba_speed           = cav->amoeba_time;
4788   level->time_magic_wall        = cav->wonderwall_time;
4789   level->time_wheel             = cav->wheel_time;
4790
4791   level->android_move_time      = cav->android_move_time;
4792   level->android_clone_time     = cav->android_clone_time;
4793   level->ball_random            = cav->ball_random;
4794   level->ball_active_initial    = cav->ball_active;
4795   level->ball_time              = cav->ball_time;
4796   level->num_ball_contents      = cav->num_ball_arrays;
4797
4798   level->lenses_score           = cav->lenses_score;
4799   level->magnify_score          = cav->magnify_score;
4800   level->slurp_score            = cav->slurp_score;
4801
4802   level->lenses_time            = cav->lenses_time;
4803   level->magnify_time           = cav->magnify_time;
4804
4805   level->wind_direction_initial =
4806     map_direction_EM_to_RND(cav->wind_direction);
4807
4808   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4809     for (j = 0; j < 8; j++)
4810       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4811         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4812
4813   map_android_clone_elements_EM_to_RND(level);
4814
4815   // convert the playfield (some elements need special treatment)
4816   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4817   {
4818     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4819
4820     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4821       new_element = EL_AMOEBA_DEAD;
4822
4823     level->field[x][y] = new_element;
4824   }
4825
4826   for (i = 0; i < MAX_PLAYERS; i++)
4827   {
4828     // in case of all players set to the same field, use the first player
4829     int nr = MAX_PLAYERS - i - 1;
4830     int jx = cav->player_x[nr];
4831     int jy = cav->player_y[nr];
4832
4833     if (jx != -1 && jy != -1)
4834       level->field[jx][jy] = EL_PLAYER_1 + nr;
4835   }
4836
4837   // time score is counted for each 10 seconds left in Emerald Mine levels
4838   level->time_score_base = 10;
4839 }
4840
4841
4842 // ----------------------------------------------------------------------------
4843 // functions for loading SP level
4844 // ----------------------------------------------------------------------------
4845
4846 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4847 {
4848   struct LevelInfo_SP *level_sp = level->native_sp_level;
4849   LevelInfoType *header = &level_sp->header;
4850   int i, x, y;
4851
4852   level_sp->width  = level->fieldx;
4853   level_sp->height = level->fieldy;
4854
4855   for (x = 0; x < level->fieldx; x++)
4856     for (y = 0; y < level->fieldy; y++)
4857       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4858
4859   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4860
4861   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4862     header->LevelTitle[i] = level->name[i];
4863   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4864
4865   header->InfotronsNeeded = level->gems_needed;
4866
4867   header->SpecialPortCount = 0;
4868
4869   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4870   {
4871     boolean gravity_port_found = FALSE;
4872     boolean gravity_port_valid = FALSE;
4873     int gravity_port_flag;
4874     int gravity_port_base_element;
4875     int element = level->field[x][y];
4876
4877     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4878         element <= EL_SP_GRAVITY_ON_PORT_UP)
4879     {
4880       gravity_port_found = TRUE;
4881       gravity_port_valid = TRUE;
4882       gravity_port_flag = 1;
4883       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4884     }
4885     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4886              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4887     {
4888       gravity_port_found = TRUE;
4889       gravity_port_valid = TRUE;
4890       gravity_port_flag = 0;
4891       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4892     }
4893     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4894              element <= EL_SP_GRAVITY_PORT_UP)
4895     {
4896       // change R'n'D style gravity inverting special port to normal port
4897       // (there are no gravity inverting ports in native Supaplex engine)
4898
4899       gravity_port_found = TRUE;
4900       gravity_port_valid = FALSE;
4901       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4902     }
4903
4904     if (gravity_port_found)
4905     {
4906       if (gravity_port_valid &&
4907           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4908       {
4909         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4910
4911         port->PortLocation = (y * level->fieldx + x) * 2;
4912         port->Gravity = gravity_port_flag;
4913
4914         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4915
4916         header->SpecialPortCount++;
4917       }
4918       else
4919       {
4920         // change special gravity port to normal port
4921
4922         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4923       }
4924
4925       level_sp->playfield[x][y] = element - EL_SP_START;
4926     }
4927   }
4928 }
4929
4930 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4931 {
4932   struct LevelInfo_SP *level_sp = level->native_sp_level;
4933   LevelInfoType *header = &level_sp->header;
4934   boolean num_invalid_elements = 0;
4935   int i, j, x, y;
4936
4937   level->fieldx = level_sp->width;
4938   level->fieldy = level_sp->height;
4939
4940   for (x = 0; x < level->fieldx; x++)
4941   {
4942     for (y = 0; y < level->fieldy; y++)
4943     {
4944       int element_old = level_sp->playfield[x][y];
4945       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4946
4947       if (element_new == EL_UNKNOWN)
4948       {
4949         num_invalid_elements++;
4950
4951         Debug("level:native:SP", "invalid element %d at position %d, %d",
4952               element_old, x, y);
4953       }
4954
4955       level->field[x][y] = element_new;
4956     }
4957   }
4958
4959   if (num_invalid_elements > 0)
4960     Warn("found %d invalid elements%s", num_invalid_elements,
4961          (!options.debug ? " (use '--debug' for more details)" : ""));
4962
4963   for (i = 0; i < MAX_PLAYERS; i++)
4964     level->initial_player_gravity[i] =
4965       (header->InitialGravity == 1 ? TRUE : FALSE);
4966
4967   // skip leading spaces
4968   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4969     if (header->LevelTitle[i] != ' ')
4970       break;
4971
4972   // copy level title
4973   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4974     level->name[j] = header->LevelTitle[i];
4975   level->name[j] = '\0';
4976
4977   // cut trailing spaces
4978   for (; j > 0; j--)
4979     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4980       level->name[j - 1] = '\0';
4981
4982   level->gems_needed = header->InfotronsNeeded;
4983
4984   for (i = 0; i < header->SpecialPortCount; i++)
4985   {
4986     SpecialPortType *port = &header->SpecialPort[i];
4987     int port_location = port->PortLocation;
4988     int gravity = port->Gravity;
4989     int port_x, port_y, port_element;
4990
4991     port_x = (port_location / 2) % level->fieldx;
4992     port_y = (port_location / 2) / level->fieldx;
4993
4994     if (port_x < 0 || port_x >= level->fieldx ||
4995         port_y < 0 || port_y >= level->fieldy)
4996     {
4997       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4998
4999       continue;
5000     }
5001
5002     port_element = level->field[port_x][port_y];
5003
5004     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5005         port_element > EL_SP_GRAVITY_PORT_UP)
5006     {
5007       Warn("no special port at position (%d, %d)", port_x, port_y);
5008
5009       continue;
5010     }
5011
5012     // change previous (wrong) gravity inverting special port to either
5013     // gravity enabling special port or gravity disabling special port
5014     level->field[port_x][port_y] +=
5015       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5016        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5017   }
5018
5019   // change special gravity ports without database entries to normal ports
5020   for (x = 0; x < level->fieldx; x++)
5021     for (y = 0; y < level->fieldy; y++)
5022       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5023           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5024         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5025
5026   level->time = 0;                      // no time limit
5027   level->amoeba_speed = 0;
5028   level->time_magic_wall = 0;
5029   level->time_wheel = 0;
5030   level->amoeba_content = EL_EMPTY;
5031
5032   // original Supaplex does not use score values -- rate by playing time
5033   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5034     level->score[i] = 0;
5035
5036   level->rate_time_over_score = TRUE;
5037
5038   // there are no yamyams in supaplex levels
5039   for (i = 0; i < level->num_yamyam_contents; i++)
5040     for (x = 0; x < 3; x++)
5041       for (y = 0; y < 3; y++)
5042         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5043 }
5044
5045 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5046 {
5047   struct LevelInfo_SP *level_sp = level->native_sp_level;
5048   struct DemoInfo_SP *demo = &level_sp->demo;
5049   int i, j;
5050
5051   // always start with reliable default values
5052   demo->is_available = FALSE;
5053   demo->length = 0;
5054
5055   if (TAPE_IS_EMPTY(tape))
5056     return;
5057
5058   demo->level_nr = tape.level_nr;       // (currently not used)
5059
5060   level_sp->header.DemoRandomSeed = tape.random_seed;
5061
5062   demo->length = 0;
5063
5064   for (i = 0; i < tape.length; i++)
5065   {
5066     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5067     int demo_repeat = tape.pos[i].delay;
5068     int demo_entries = (demo_repeat + 15) / 16;
5069
5070     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5071     {
5072       Warn("tape truncated: size exceeds maximum SP demo size %d",
5073            SP_MAX_TAPE_LEN);
5074
5075       break;
5076     }
5077
5078     for (j = 0; j < demo_repeat / 16; j++)
5079       demo->data[demo->length++] = 0xf0 | demo_action;
5080
5081     if (demo_repeat % 16)
5082       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5083   }
5084
5085   demo->is_available = TRUE;
5086 }
5087
5088 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5089 {
5090   struct LevelInfo_SP *level_sp = level->native_sp_level;
5091   struct DemoInfo_SP *demo = &level_sp->demo;
5092   char *filename = level->file_info.filename;
5093   int i;
5094
5095   // always start with reliable default values
5096   setTapeInfoToDefaults();
5097
5098   if (!demo->is_available)
5099     return;
5100
5101   tape.level_nr = demo->level_nr;       // (currently not used)
5102   tape.random_seed = level_sp->header.DemoRandomSeed;
5103
5104   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5105
5106   tape.counter = 0;
5107   tape.pos[tape.counter].delay = 0;
5108
5109   for (i = 0; i < demo->length; i++)
5110   {
5111     int demo_action = demo->data[i] & 0x0f;
5112     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5113     int tape_action = map_key_SP_to_RND(demo_action);
5114     int tape_repeat = demo_repeat + 1;
5115     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5116     boolean success = 0;
5117     int j;
5118
5119     for (j = 0; j < tape_repeat; j++)
5120       success = TapeAddAction(action);
5121
5122     if (!success)
5123     {
5124       Warn("SP demo truncated: size exceeds maximum tape size %d",
5125            MAX_TAPE_LEN);
5126
5127       break;
5128     }
5129   }
5130
5131   TapeHaltRecording();
5132 }
5133
5134
5135 // ----------------------------------------------------------------------------
5136 // functions for loading MM level
5137 // ----------------------------------------------------------------------------
5138
5139 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5140 {
5141   struct LevelInfo_MM *level_mm = level->native_mm_level;
5142   int i, x, y;
5143
5144   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5145   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5146
5147   level_mm->time = level->time;
5148   level_mm->kettles_needed = level->gems_needed;
5149   level_mm->auto_count_kettles = level->auto_count_gems;
5150
5151   level_mm->mm_laser_red   = level->mm_laser_red;
5152   level_mm->mm_laser_green = level->mm_laser_green;
5153   level_mm->mm_laser_blue  = level->mm_laser_blue;
5154
5155   level_mm->df_laser_red   = level->df_laser_red;
5156   level_mm->df_laser_green = level->df_laser_green;
5157   level_mm->df_laser_blue  = level->df_laser_blue;
5158
5159   strcpy(level_mm->name, level->name);
5160   strcpy(level_mm->author, level->author);
5161
5162   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5163   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5164   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5165   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5166   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5167
5168   level_mm->amoeba_speed = level->amoeba_speed;
5169   level_mm->time_fuse    = level->mm_time_fuse;
5170   level_mm->time_bomb    = level->mm_time_bomb;
5171   level_mm->time_ball    = level->mm_time_ball;
5172   level_mm->time_block   = level->mm_time_block;
5173
5174   level_mm->num_ball_contents = level->num_mm_ball_contents;
5175   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5176   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5177   level_mm->explode_ball = level->explode_mm_ball;
5178
5179   for (i = 0; i < level->num_mm_ball_contents; i++)
5180     level_mm->ball_content[i] =
5181       map_element_RND_to_MM(level->mm_ball_content[i]);
5182
5183   for (x = 0; x < level->fieldx; x++)
5184     for (y = 0; y < level->fieldy; y++)
5185       Ur[x][y] =
5186         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5187 }
5188
5189 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5190 {
5191   struct LevelInfo_MM *level_mm = level->native_mm_level;
5192   int i, x, y;
5193
5194   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5195   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5196
5197   level->time = level_mm->time;
5198   level->gems_needed = level_mm->kettles_needed;
5199   level->auto_count_gems = level_mm->auto_count_kettles;
5200
5201   level->mm_laser_red   = level_mm->mm_laser_red;
5202   level->mm_laser_green = level_mm->mm_laser_green;
5203   level->mm_laser_blue  = level_mm->mm_laser_blue;
5204
5205   level->df_laser_red   = level_mm->df_laser_red;
5206   level->df_laser_green = level_mm->df_laser_green;
5207   level->df_laser_blue  = level_mm->df_laser_blue;
5208
5209   strcpy(level->name, level_mm->name);
5210
5211   // only overwrite author from 'levelinfo.conf' if author defined in level
5212   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5213     strcpy(level->author, level_mm->author);
5214
5215   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5216   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5217   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5218   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5219   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5220
5221   level->amoeba_speed  = level_mm->amoeba_speed;
5222   level->mm_time_fuse  = level_mm->time_fuse;
5223   level->mm_time_bomb  = level_mm->time_bomb;
5224   level->mm_time_ball  = level_mm->time_ball;
5225   level->mm_time_block = level_mm->time_block;
5226
5227   level->num_mm_ball_contents = level_mm->num_ball_contents;
5228   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5229   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5230   level->explode_mm_ball = level_mm->explode_ball;
5231
5232   for (i = 0; i < level->num_mm_ball_contents; i++)
5233     level->mm_ball_content[i] =
5234       map_element_MM_to_RND(level_mm->ball_content[i]);
5235
5236   for (x = 0; x < level->fieldx; x++)
5237     for (y = 0; y < level->fieldy; y++)
5238       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5239 }
5240
5241
5242 // ----------------------------------------------------------------------------
5243 // functions for loading DC level
5244 // ----------------------------------------------------------------------------
5245
5246 #define DC_LEVEL_HEADER_SIZE            344
5247
5248 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5249                                         boolean init)
5250 {
5251   static int last_data_encoded;
5252   static int offset1;
5253   static int offset2;
5254   int diff;
5255   int diff_hi, diff_lo;
5256   int data_hi, data_lo;
5257   unsigned short data_decoded;
5258
5259   if (init)
5260   {
5261     last_data_encoded = 0;
5262     offset1 = -1;
5263     offset2 = 0;
5264
5265     return 0;
5266   }
5267
5268   diff = data_encoded - last_data_encoded;
5269   diff_hi = diff & ~0xff;
5270   diff_lo = diff &  0xff;
5271
5272   offset2 += diff_lo;
5273
5274   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5275   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5276   data_hi = data_hi & 0xff00;
5277
5278   data_decoded = data_hi | data_lo;
5279
5280   last_data_encoded = data_encoded;
5281
5282   offset1 = (offset1 + 1) % 31;
5283   offset2 = offset2 & 0xff;
5284
5285   return data_decoded;
5286 }
5287
5288 static int getMappedElement_DC(int element)
5289 {
5290   switch (element)
5291   {
5292     case 0x0000:
5293       element = EL_ROCK;
5294       break;
5295
5296       // 0x0117 - 0x036e: (?)
5297       // EL_DIAMOND
5298
5299       // 0x042d - 0x0684: (?)
5300       // EL_EMERALD
5301
5302     case 0x06f1:
5303       element = EL_NUT;
5304       break;
5305
5306     case 0x074c:
5307       element = EL_BOMB;
5308       break;
5309
5310     case 0x07a4:
5311       element = EL_PEARL;
5312       break;
5313
5314     case 0x0823:
5315       element = EL_CRYSTAL;
5316       break;
5317
5318     case 0x0e77:        // quicksand (boulder)
5319       element = EL_QUICKSAND_FAST_FULL;
5320       break;
5321
5322     case 0x0e99:        // slow quicksand (boulder)
5323       element = EL_QUICKSAND_FULL;
5324       break;
5325
5326     case 0x0ed2:
5327       element = EL_EM_EXIT_OPEN;
5328       break;
5329
5330     case 0x0ee3:
5331       element = EL_EM_EXIT_CLOSED;
5332       break;
5333
5334     case 0x0eeb:
5335       element = EL_EM_STEEL_EXIT_OPEN;
5336       break;
5337
5338     case 0x0efc:
5339       element = EL_EM_STEEL_EXIT_CLOSED;
5340       break;
5341
5342     case 0x0f4f:        // dynamite (lit 1)
5343       element = EL_EM_DYNAMITE_ACTIVE;
5344       break;
5345
5346     case 0x0f57:        // dynamite (lit 2)
5347       element = EL_EM_DYNAMITE_ACTIVE;
5348       break;
5349
5350     case 0x0f5f:        // dynamite (lit 3)
5351       element = EL_EM_DYNAMITE_ACTIVE;
5352       break;
5353
5354     case 0x0f67:        // dynamite (lit 4)
5355       element = EL_EM_DYNAMITE_ACTIVE;
5356       break;
5357
5358     case 0x0f81:
5359     case 0x0f82:
5360     case 0x0f83:
5361     case 0x0f84:
5362       element = EL_AMOEBA_WET;
5363       break;
5364
5365     case 0x0f85:
5366       element = EL_AMOEBA_DROP;
5367       break;
5368
5369     case 0x0fb9:
5370       element = EL_DC_MAGIC_WALL;
5371       break;
5372
5373     case 0x0fd0:
5374       element = EL_SPACESHIP_UP;
5375       break;
5376
5377     case 0x0fd9:
5378       element = EL_SPACESHIP_DOWN;
5379       break;
5380
5381     case 0x0ff1:
5382       element = EL_SPACESHIP_LEFT;
5383       break;
5384
5385     case 0x0ff9:
5386       element = EL_SPACESHIP_RIGHT;
5387       break;
5388
5389     case 0x1057:
5390       element = EL_BUG_UP;
5391       break;
5392
5393     case 0x1060:
5394       element = EL_BUG_DOWN;
5395       break;
5396
5397     case 0x1078:
5398       element = EL_BUG_LEFT;
5399       break;
5400
5401     case 0x1080:
5402       element = EL_BUG_RIGHT;
5403       break;
5404
5405     case 0x10de:
5406       element = EL_MOLE_UP;
5407       break;
5408
5409     case 0x10e7:
5410       element = EL_MOLE_DOWN;
5411       break;
5412
5413     case 0x10ff:
5414       element = EL_MOLE_LEFT;
5415       break;
5416
5417     case 0x1107:
5418       element = EL_MOLE_RIGHT;
5419       break;
5420
5421     case 0x11c0:
5422       element = EL_ROBOT;
5423       break;
5424
5425     case 0x13f5:
5426       element = EL_YAMYAM_UP;
5427       break;
5428
5429     case 0x1425:
5430       element = EL_SWITCHGATE_OPEN;
5431       break;
5432
5433     case 0x1426:
5434       element = EL_SWITCHGATE_CLOSED;
5435       break;
5436
5437     case 0x1437:
5438       element = EL_DC_SWITCHGATE_SWITCH_UP;
5439       break;
5440
5441     case 0x143a:
5442       element = EL_TIMEGATE_CLOSED;
5443       break;
5444
5445     case 0x144c:        // conveyor belt switch (green)
5446       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5447       break;
5448
5449     case 0x144f:        // conveyor belt switch (red)
5450       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5451       break;
5452
5453     case 0x1452:        // conveyor belt switch (blue)
5454       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5455       break;
5456
5457     case 0x145b:
5458       element = EL_CONVEYOR_BELT_3_MIDDLE;
5459       break;
5460
5461     case 0x1463:
5462       element = EL_CONVEYOR_BELT_3_LEFT;
5463       break;
5464
5465     case 0x146b:
5466       element = EL_CONVEYOR_BELT_3_RIGHT;
5467       break;
5468
5469     case 0x1473:
5470       element = EL_CONVEYOR_BELT_1_MIDDLE;
5471       break;
5472
5473     case 0x147b:
5474       element = EL_CONVEYOR_BELT_1_LEFT;
5475       break;
5476
5477     case 0x1483:
5478       element = EL_CONVEYOR_BELT_1_RIGHT;
5479       break;
5480
5481     case 0x148b:
5482       element = EL_CONVEYOR_BELT_4_MIDDLE;
5483       break;
5484
5485     case 0x1493:
5486       element = EL_CONVEYOR_BELT_4_LEFT;
5487       break;
5488
5489     case 0x149b:
5490       element = EL_CONVEYOR_BELT_4_RIGHT;
5491       break;
5492
5493     case 0x14ac:
5494       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5495       break;
5496
5497     case 0x14bd:
5498       element = EL_EXPANDABLE_WALL_VERTICAL;
5499       break;
5500
5501     case 0x14c6:
5502       element = EL_EXPANDABLE_WALL_ANY;
5503       break;
5504
5505     case 0x14ce:        // growing steel wall (left/right)
5506       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5507       break;
5508
5509     case 0x14df:        // growing steel wall (up/down)
5510       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5511       break;
5512
5513     case 0x14e8:        // growing steel wall (up/down/left/right)
5514       element = EL_EXPANDABLE_STEELWALL_ANY;
5515       break;
5516
5517     case 0x14e9:
5518       element = EL_SHIELD_DEADLY;
5519       break;
5520
5521     case 0x1501:
5522       element = EL_EXTRA_TIME;
5523       break;
5524
5525     case 0x154f:
5526       element = EL_ACID;
5527       break;
5528
5529     case 0x1577:
5530       element = EL_EMPTY_SPACE;
5531       break;
5532
5533     case 0x1578:        // quicksand (empty)
5534       element = EL_QUICKSAND_FAST_EMPTY;
5535       break;
5536
5537     case 0x1579:        // slow quicksand (empty)
5538       element = EL_QUICKSAND_EMPTY;
5539       break;
5540
5541       // 0x157c - 0x158b:
5542       // EL_SAND
5543
5544       // 0x1590 - 0x159f:
5545       // EL_DC_LANDMINE
5546
5547     case 0x15a0:
5548       element = EL_EM_DYNAMITE;
5549       break;
5550
5551     case 0x15a1:        // key (red)
5552       element = EL_EM_KEY_1;
5553       break;
5554
5555     case 0x15a2:        // key (yellow)
5556       element = EL_EM_KEY_2;
5557       break;
5558
5559     case 0x15a3:        // key (blue)
5560       element = EL_EM_KEY_4;
5561       break;
5562
5563     case 0x15a4:        // key (green)
5564       element = EL_EM_KEY_3;
5565       break;
5566
5567     case 0x15a5:        // key (white)
5568       element = EL_DC_KEY_WHITE;
5569       break;
5570
5571     case 0x15a6:
5572       element = EL_WALL_SLIPPERY;
5573       break;
5574
5575     case 0x15a7:
5576       element = EL_WALL;
5577       break;
5578
5579     case 0x15a8:        // wall (not round)
5580       element = EL_WALL;
5581       break;
5582
5583     case 0x15a9:        // (blue)
5584       element = EL_CHAR_A;
5585       break;
5586
5587     case 0x15aa:        // (blue)
5588       element = EL_CHAR_B;
5589       break;
5590
5591     case 0x15ab:        // (blue)
5592       element = EL_CHAR_C;
5593       break;
5594
5595     case 0x15ac:        // (blue)
5596       element = EL_CHAR_D;
5597       break;
5598
5599     case 0x15ad:        // (blue)
5600       element = EL_CHAR_E;
5601       break;
5602
5603     case 0x15ae:        // (blue)
5604       element = EL_CHAR_F;
5605       break;
5606
5607     case 0x15af:        // (blue)
5608       element = EL_CHAR_G;
5609       break;
5610
5611     case 0x15b0:        // (blue)
5612       element = EL_CHAR_H;
5613       break;
5614
5615     case 0x15b1:        // (blue)
5616       element = EL_CHAR_I;
5617       break;
5618
5619     case 0x15b2:        // (blue)
5620       element = EL_CHAR_J;
5621       break;
5622
5623     case 0x15b3:        // (blue)
5624       element = EL_CHAR_K;
5625       break;
5626
5627     case 0x15b4:        // (blue)
5628       element = EL_CHAR_L;
5629       break;
5630
5631     case 0x15b5:        // (blue)
5632       element = EL_CHAR_M;
5633       break;
5634
5635     case 0x15b6:        // (blue)
5636       element = EL_CHAR_N;
5637       break;
5638
5639     case 0x15b7:        // (blue)
5640       element = EL_CHAR_O;
5641       break;
5642
5643     case 0x15b8:        // (blue)
5644       element = EL_CHAR_P;
5645       break;
5646
5647     case 0x15b9:        // (blue)
5648       element = EL_CHAR_Q;
5649       break;
5650
5651     case 0x15ba:        // (blue)
5652       element = EL_CHAR_R;
5653       break;
5654
5655     case 0x15bb:        // (blue)
5656       element = EL_CHAR_S;
5657       break;
5658
5659     case 0x15bc:        // (blue)
5660       element = EL_CHAR_T;
5661       break;
5662
5663     case 0x15bd:        // (blue)
5664       element = EL_CHAR_U;
5665       break;
5666
5667     case 0x15be:        // (blue)
5668       element = EL_CHAR_V;
5669       break;
5670
5671     case 0x15bf:        // (blue)
5672       element = EL_CHAR_W;
5673       break;
5674
5675     case 0x15c0:        // (blue)
5676       element = EL_CHAR_X;
5677       break;
5678
5679     case 0x15c1:        // (blue)
5680       element = EL_CHAR_Y;
5681       break;
5682
5683     case 0x15c2:        // (blue)
5684       element = EL_CHAR_Z;
5685       break;
5686
5687     case 0x15c3:        // (blue)
5688       element = EL_CHAR_AUMLAUT;
5689       break;
5690
5691     case 0x15c4:        // (blue)
5692       element = EL_CHAR_OUMLAUT;
5693       break;
5694
5695     case 0x15c5:        // (blue)
5696       element = EL_CHAR_UUMLAUT;
5697       break;
5698
5699     case 0x15c6:        // (blue)
5700       element = EL_CHAR_0;
5701       break;
5702
5703     case 0x15c7:        // (blue)
5704       element = EL_CHAR_1;
5705       break;
5706
5707     case 0x15c8:        // (blue)
5708       element = EL_CHAR_2;
5709       break;
5710
5711     case 0x15c9:        // (blue)
5712       element = EL_CHAR_3;
5713       break;
5714
5715     case 0x15ca:        // (blue)
5716       element = EL_CHAR_4;
5717       break;
5718
5719     case 0x15cb:        // (blue)
5720       element = EL_CHAR_5;
5721       break;
5722
5723     case 0x15cc:        // (blue)
5724       element = EL_CHAR_6;
5725       break;
5726
5727     case 0x15cd:        // (blue)
5728       element = EL_CHAR_7;
5729       break;
5730
5731     case 0x15ce:        // (blue)
5732       element = EL_CHAR_8;
5733       break;
5734
5735     case 0x15cf:        // (blue)
5736       element = EL_CHAR_9;
5737       break;
5738
5739     case 0x15d0:        // (blue)
5740       element = EL_CHAR_PERIOD;
5741       break;
5742
5743     case 0x15d1:        // (blue)
5744       element = EL_CHAR_EXCLAM;
5745       break;
5746
5747     case 0x15d2:        // (blue)
5748       element = EL_CHAR_COLON;
5749       break;
5750
5751     case 0x15d3:        // (blue)
5752       element = EL_CHAR_LESS;
5753       break;
5754
5755     case 0x15d4:        // (blue)
5756       element = EL_CHAR_GREATER;
5757       break;
5758
5759     case 0x15d5:        // (blue)
5760       element = EL_CHAR_QUESTION;
5761       break;
5762
5763     case 0x15d6:        // (blue)
5764       element = EL_CHAR_COPYRIGHT;
5765       break;
5766
5767     case 0x15d7:        // (blue)
5768       element = EL_CHAR_UP;
5769       break;
5770
5771     case 0x15d8:        // (blue)
5772       element = EL_CHAR_DOWN;
5773       break;
5774
5775     case 0x15d9:        // (blue)
5776       element = EL_CHAR_BUTTON;
5777       break;
5778
5779     case 0x15da:        // (blue)
5780       element = EL_CHAR_PLUS;
5781       break;
5782
5783     case 0x15db:        // (blue)
5784       element = EL_CHAR_MINUS;
5785       break;
5786
5787     case 0x15dc:        // (blue)
5788       element = EL_CHAR_APOSTROPHE;
5789       break;
5790
5791     case 0x15dd:        // (blue)
5792       element = EL_CHAR_PARENLEFT;
5793       break;
5794
5795     case 0x15de:        // (blue)
5796       element = EL_CHAR_PARENRIGHT;
5797       break;
5798
5799     case 0x15df:        // (green)
5800       element = EL_CHAR_A;
5801       break;
5802
5803     case 0x15e0:        // (green)
5804       element = EL_CHAR_B;
5805       break;
5806
5807     case 0x15e1:        // (green)
5808       element = EL_CHAR_C;
5809       break;
5810
5811     case 0x15e2:        // (green)
5812       element = EL_CHAR_D;
5813       break;
5814
5815     case 0x15e3:        // (green)
5816       element = EL_CHAR_E;
5817       break;
5818
5819     case 0x15e4:        // (green)
5820       element = EL_CHAR_F;
5821       break;
5822
5823     case 0x15e5:        // (green)
5824       element = EL_CHAR_G;
5825       break;
5826
5827     case 0x15e6:        // (green)
5828       element = EL_CHAR_H;
5829       break;
5830
5831     case 0x15e7:        // (green)
5832       element = EL_CHAR_I;
5833       break;
5834
5835     case 0x15e8:        // (green)
5836       element = EL_CHAR_J;
5837       break;
5838
5839     case 0x15e9:        // (green)
5840       element = EL_CHAR_K;
5841       break;
5842
5843     case 0x15ea:        // (green)
5844       element = EL_CHAR_L;
5845       break;
5846
5847     case 0x15eb:        // (green)
5848       element = EL_CHAR_M;
5849       break;
5850
5851     case 0x15ec:        // (green)
5852       element = EL_CHAR_N;
5853       break;
5854
5855     case 0x15ed:        // (green)
5856       element = EL_CHAR_O;
5857       break;
5858
5859     case 0x15ee:        // (green)
5860       element = EL_CHAR_P;
5861       break;
5862
5863     case 0x15ef:        // (green)
5864       element = EL_CHAR_Q;
5865       break;
5866
5867     case 0x15f0:        // (green)
5868       element = EL_CHAR_R;
5869       break;
5870
5871     case 0x15f1:        // (green)
5872       element = EL_CHAR_S;
5873       break;
5874
5875     case 0x15f2:        // (green)
5876       element = EL_CHAR_T;
5877       break;
5878
5879     case 0x15f3:        // (green)
5880       element = EL_CHAR_U;
5881       break;
5882
5883     case 0x15f4:        // (green)
5884       element = EL_CHAR_V;
5885       break;
5886
5887     case 0x15f5:        // (green)
5888       element = EL_CHAR_W;
5889       break;
5890
5891     case 0x15f6:        // (green)
5892       element = EL_CHAR_X;
5893       break;
5894
5895     case 0x15f7:        // (green)
5896       element = EL_CHAR_Y;
5897       break;
5898
5899     case 0x15f8:        // (green)
5900       element = EL_CHAR_Z;
5901       break;
5902
5903     case 0x15f9:        // (green)
5904       element = EL_CHAR_AUMLAUT;
5905       break;
5906
5907     case 0x15fa:        // (green)
5908       element = EL_CHAR_OUMLAUT;
5909       break;
5910
5911     case 0x15fb:        // (green)
5912       element = EL_CHAR_UUMLAUT;
5913       break;
5914
5915     case 0x15fc:        // (green)
5916       element = EL_CHAR_0;
5917       break;
5918
5919     case 0x15fd:        // (green)
5920       element = EL_CHAR_1;
5921       break;
5922
5923     case 0x15fe:        // (green)
5924       element = EL_CHAR_2;
5925       break;
5926
5927     case 0x15ff:        // (green)
5928       element = EL_CHAR_3;
5929       break;
5930
5931     case 0x1600:        // (green)
5932       element = EL_CHAR_4;
5933       break;
5934
5935     case 0x1601:        // (green)
5936       element = EL_CHAR_5;
5937       break;
5938
5939     case 0x1602:        // (green)
5940       element = EL_CHAR_6;
5941       break;
5942
5943     case 0x1603:        // (green)
5944       element = EL_CHAR_7;
5945       break;
5946
5947     case 0x1604:        // (green)
5948       element = EL_CHAR_8;
5949       break;
5950
5951     case 0x1605:        // (green)
5952       element = EL_CHAR_9;
5953       break;
5954
5955     case 0x1606:        // (green)
5956       element = EL_CHAR_PERIOD;
5957       break;
5958
5959     case 0x1607:        // (green)
5960       element = EL_CHAR_EXCLAM;
5961       break;
5962
5963     case 0x1608:        // (green)
5964       element = EL_CHAR_COLON;
5965       break;
5966
5967     case 0x1609:        // (green)
5968       element = EL_CHAR_LESS;
5969       break;
5970
5971     case 0x160a:        // (green)
5972       element = EL_CHAR_GREATER;
5973       break;
5974
5975     case 0x160b:        // (green)
5976       element = EL_CHAR_QUESTION;
5977       break;
5978
5979     case 0x160c:        // (green)
5980       element = EL_CHAR_COPYRIGHT;
5981       break;
5982
5983     case 0x160d:        // (green)
5984       element = EL_CHAR_UP;
5985       break;
5986
5987     case 0x160e:        // (green)
5988       element = EL_CHAR_DOWN;
5989       break;
5990
5991     case 0x160f:        // (green)
5992       element = EL_CHAR_BUTTON;
5993       break;
5994
5995     case 0x1610:        // (green)
5996       element = EL_CHAR_PLUS;
5997       break;
5998
5999     case 0x1611:        // (green)
6000       element = EL_CHAR_MINUS;
6001       break;
6002
6003     case 0x1612:        // (green)
6004       element = EL_CHAR_APOSTROPHE;
6005       break;
6006
6007     case 0x1613:        // (green)
6008       element = EL_CHAR_PARENLEFT;
6009       break;
6010
6011     case 0x1614:        // (green)
6012       element = EL_CHAR_PARENRIGHT;
6013       break;
6014
6015     case 0x1615:        // (blue steel)
6016       element = EL_STEEL_CHAR_A;
6017       break;
6018
6019     case 0x1616:        // (blue steel)
6020       element = EL_STEEL_CHAR_B;
6021       break;
6022
6023     case 0x1617:        // (blue steel)
6024       element = EL_STEEL_CHAR_C;
6025       break;
6026
6027     case 0x1618:        // (blue steel)
6028       element = EL_STEEL_CHAR_D;
6029       break;
6030
6031     case 0x1619:        // (blue steel)
6032       element = EL_STEEL_CHAR_E;
6033       break;
6034
6035     case 0x161a:        // (blue steel)
6036       element = EL_STEEL_CHAR_F;
6037       break;
6038
6039     case 0x161b:        // (blue steel)
6040       element = EL_STEEL_CHAR_G;
6041       break;
6042
6043     case 0x161c:        // (blue steel)
6044       element = EL_STEEL_CHAR_H;
6045       break;
6046
6047     case 0x161d:        // (blue steel)
6048       element = EL_STEEL_CHAR_I;
6049       break;
6050
6051     case 0x161e:        // (blue steel)
6052       element = EL_STEEL_CHAR_J;
6053       break;
6054
6055     case 0x161f:        // (blue steel)
6056       element = EL_STEEL_CHAR_K;
6057       break;
6058
6059     case 0x1620:        // (blue steel)
6060       element = EL_STEEL_CHAR_L;
6061       break;
6062
6063     case 0x1621:        // (blue steel)
6064       element = EL_STEEL_CHAR_M;
6065       break;
6066
6067     case 0x1622:        // (blue steel)
6068       element = EL_STEEL_CHAR_N;
6069       break;
6070
6071     case 0x1623:        // (blue steel)
6072       element = EL_STEEL_CHAR_O;
6073       break;
6074
6075     case 0x1624:        // (blue steel)
6076       element = EL_STEEL_CHAR_P;
6077       break;
6078
6079     case 0x1625:        // (blue steel)
6080       element = EL_STEEL_CHAR_Q;
6081       break;
6082
6083     case 0x1626:        // (blue steel)
6084       element = EL_STEEL_CHAR_R;
6085       break;
6086
6087     case 0x1627:        // (blue steel)
6088       element = EL_STEEL_CHAR_S;
6089       break;
6090
6091     case 0x1628:        // (blue steel)
6092       element = EL_STEEL_CHAR_T;
6093       break;
6094
6095     case 0x1629:        // (blue steel)
6096       element = EL_STEEL_CHAR_U;
6097       break;
6098
6099     case 0x162a:        // (blue steel)
6100       element = EL_STEEL_CHAR_V;
6101       break;
6102
6103     case 0x162b:        // (blue steel)
6104       element = EL_STEEL_CHAR_W;
6105       break;
6106
6107     case 0x162c:        // (blue steel)
6108       element = EL_STEEL_CHAR_X;
6109       break;
6110
6111     case 0x162d:        // (blue steel)
6112       element = EL_STEEL_CHAR_Y;
6113       break;
6114
6115     case 0x162e:        // (blue steel)
6116       element = EL_STEEL_CHAR_Z;
6117       break;
6118
6119     case 0x162f:        // (blue steel)
6120       element = EL_STEEL_CHAR_AUMLAUT;
6121       break;
6122
6123     case 0x1630:        // (blue steel)
6124       element = EL_STEEL_CHAR_OUMLAUT;
6125       break;
6126
6127     case 0x1631:        // (blue steel)
6128       element = EL_STEEL_CHAR_UUMLAUT;
6129       break;
6130
6131     case 0x1632:        // (blue steel)
6132       element = EL_STEEL_CHAR_0;
6133       break;
6134
6135     case 0x1633:        // (blue steel)
6136       element = EL_STEEL_CHAR_1;
6137       break;
6138
6139     case 0x1634:        // (blue steel)
6140       element = EL_STEEL_CHAR_2;
6141       break;
6142
6143     case 0x1635:        // (blue steel)
6144       element = EL_STEEL_CHAR_3;
6145       break;
6146
6147     case 0x1636:        // (blue steel)
6148       element = EL_STEEL_CHAR_4;
6149       break;
6150
6151     case 0x1637:        // (blue steel)
6152       element = EL_STEEL_CHAR_5;
6153       break;
6154
6155     case 0x1638:        // (blue steel)
6156       element = EL_STEEL_CHAR_6;
6157       break;
6158
6159     case 0x1639:        // (blue steel)
6160       element = EL_STEEL_CHAR_7;
6161       break;
6162
6163     case 0x163a:        // (blue steel)
6164       element = EL_STEEL_CHAR_8;
6165       break;
6166
6167     case 0x163b:        // (blue steel)
6168       element = EL_STEEL_CHAR_9;
6169       break;
6170
6171     case 0x163c:        // (blue steel)
6172       element = EL_STEEL_CHAR_PERIOD;
6173       break;
6174
6175     case 0x163d:        // (blue steel)
6176       element = EL_STEEL_CHAR_EXCLAM;
6177       break;
6178
6179     case 0x163e:        // (blue steel)
6180       element = EL_STEEL_CHAR_COLON;
6181       break;
6182
6183     case 0x163f:        // (blue steel)
6184       element = EL_STEEL_CHAR_LESS;
6185       break;
6186
6187     case 0x1640:        // (blue steel)
6188       element = EL_STEEL_CHAR_GREATER;
6189       break;
6190
6191     case 0x1641:        // (blue steel)
6192       element = EL_STEEL_CHAR_QUESTION;
6193       break;
6194
6195     case 0x1642:        // (blue steel)
6196       element = EL_STEEL_CHAR_COPYRIGHT;
6197       break;
6198
6199     case 0x1643:        // (blue steel)
6200       element = EL_STEEL_CHAR_UP;
6201       break;
6202
6203     case 0x1644:        // (blue steel)
6204       element = EL_STEEL_CHAR_DOWN;
6205       break;
6206
6207     case 0x1645:        // (blue steel)
6208       element = EL_STEEL_CHAR_BUTTON;
6209       break;
6210
6211     case 0x1646:        // (blue steel)
6212       element = EL_STEEL_CHAR_PLUS;
6213       break;
6214
6215     case 0x1647:        // (blue steel)
6216       element = EL_STEEL_CHAR_MINUS;
6217       break;
6218
6219     case 0x1648:        // (blue steel)
6220       element = EL_STEEL_CHAR_APOSTROPHE;
6221       break;
6222
6223     case 0x1649:        // (blue steel)
6224       element = EL_STEEL_CHAR_PARENLEFT;
6225       break;
6226
6227     case 0x164a:        // (blue steel)
6228       element = EL_STEEL_CHAR_PARENRIGHT;
6229       break;
6230
6231     case 0x164b:        // (green steel)
6232       element = EL_STEEL_CHAR_A;
6233       break;
6234
6235     case 0x164c:        // (green steel)
6236       element = EL_STEEL_CHAR_B;
6237       break;
6238
6239     case 0x164d:        // (green steel)
6240       element = EL_STEEL_CHAR_C;
6241       break;
6242
6243     case 0x164e:        // (green steel)
6244       element = EL_STEEL_CHAR_D;
6245       break;
6246
6247     case 0x164f:        // (green steel)
6248       element = EL_STEEL_CHAR_E;
6249       break;
6250
6251     case 0x1650:        // (green steel)
6252       element = EL_STEEL_CHAR_F;
6253       break;
6254
6255     case 0x1651:        // (green steel)
6256       element = EL_STEEL_CHAR_G;
6257       break;
6258
6259     case 0x1652:        // (green steel)
6260       element = EL_STEEL_CHAR_H;
6261       break;
6262
6263     case 0x1653:        // (green steel)
6264       element = EL_STEEL_CHAR_I;
6265       break;
6266
6267     case 0x1654:        // (green steel)
6268       element = EL_STEEL_CHAR_J;
6269       break;
6270
6271     case 0x1655:        // (green steel)
6272       element = EL_STEEL_CHAR_K;
6273       break;
6274
6275     case 0x1656:        // (green steel)
6276       element = EL_STEEL_CHAR_L;
6277       break;
6278
6279     case 0x1657:        // (green steel)
6280       element = EL_STEEL_CHAR_M;
6281       break;
6282
6283     case 0x1658:        // (green steel)
6284       element = EL_STEEL_CHAR_N;
6285       break;
6286
6287     case 0x1659:        // (green steel)
6288       element = EL_STEEL_CHAR_O;
6289       break;
6290
6291     case 0x165a:        // (green steel)
6292       element = EL_STEEL_CHAR_P;
6293       break;
6294
6295     case 0x165b:        // (green steel)
6296       element = EL_STEEL_CHAR_Q;
6297       break;
6298
6299     case 0x165c:        // (green steel)
6300       element = EL_STEEL_CHAR_R;
6301       break;
6302
6303     case 0x165d:        // (green steel)
6304       element = EL_STEEL_CHAR_S;
6305       break;
6306
6307     case 0x165e:        // (green steel)
6308       element = EL_STEEL_CHAR_T;
6309       break;
6310
6311     case 0x165f:        // (green steel)
6312       element = EL_STEEL_CHAR_U;
6313       break;
6314
6315     case 0x1660:        // (green steel)
6316       element = EL_STEEL_CHAR_V;
6317       break;
6318
6319     case 0x1661:        // (green steel)
6320       element = EL_STEEL_CHAR_W;
6321       break;
6322
6323     case 0x1662:        // (green steel)
6324       element = EL_STEEL_CHAR_X;
6325       break;
6326
6327     case 0x1663:        // (green steel)
6328       element = EL_STEEL_CHAR_Y;
6329       break;
6330
6331     case 0x1664:        // (green steel)
6332       element = EL_STEEL_CHAR_Z;
6333       break;
6334
6335     case 0x1665:        // (green steel)
6336       element = EL_STEEL_CHAR_AUMLAUT;
6337       break;
6338
6339     case 0x1666:        // (green steel)
6340       element = EL_STEEL_CHAR_OUMLAUT;
6341       break;
6342
6343     case 0x1667:        // (green steel)
6344       element = EL_STEEL_CHAR_UUMLAUT;
6345       break;
6346
6347     case 0x1668:        // (green steel)
6348       element = EL_STEEL_CHAR_0;
6349       break;
6350
6351     case 0x1669:        // (green steel)
6352       element = EL_STEEL_CHAR_1;
6353       break;
6354
6355     case 0x166a:        // (green steel)
6356       element = EL_STEEL_CHAR_2;
6357       break;
6358
6359     case 0x166b:        // (green steel)
6360       element = EL_STEEL_CHAR_3;
6361       break;
6362
6363     case 0x166c:        // (green steel)
6364       element = EL_STEEL_CHAR_4;
6365       break;
6366
6367     case 0x166d:        // (green steel)
6368       element = EL_STEEL_CHAR_5;
6369       break;
6370
6371     case 0x166e:        // (green steel)
6372       element = EL_STEEL_CHAR_6;
6373       break;
6374
6375     case 0x166f:        // (green steel)
6376       element = EL_STEEL_CHAR_7;
6377       break;
6378
6379     case 0x1670:        // (green steel)
6380       element = EL_STEEL_CHAR_8;
6381       break;
6382
6383     case 0x1671:        // (green steel)
6384       element = EL_STEEL_CHAR_9;
6385       break;
6386
6387     case 0x1672:        // (green steel)
6388       element = EL_STEEL_CHAR_PERIOD;
6389       break;
6390
6391     case 0x1673:        // (green steel)
6392       element = EL_STEEL_CHAR_EXCLAM;
6393       break;
6394
6395     case 0x1674:        // (green steel)
6396       element = EL_STEEL_CHAR_COLON;
6397       break;
6398
6399     case 0x1675:        // (green steel)
6400       element = EL_STEEL_CHAR_LESS;
6401       break;
6402
6403     case 0x1676:        // (green steel)
6404       element = EL_STEEL_CHAR_GREATER;
6405       break;
6406
6407     case 0x1677:        // (green steel)
6408       element = EL_STEEL_CHAR_QUESTION;
6409       break;
6410
6411     case 0x1678:        // (green steel)
6412       element = EL_STEEL_CHAR_COPYRIGHT;
6413       break;
6414
6415     case 0x1679:        // (green steel)
6416       element = EL_STEEL_CHAR_UP;
6417       break;
6418
6419     case 0x167a:        // (green steel)
6420       element = EL_STEEL_CHAR_DOWN;
6421       break;
6422
6423     case 0x167b:        // (green steel)
6424       element = EL_STEEL_CHAR_BUTTON;
6425       break;
6426
6427     case 0x167c:        // (green steel)
6428       element = EL_STEEL_CHAR_PLUS;
6429       break;
6430
6431     case 0x167d:        // (green steel)
6432       element = EL_STEEL_CHAR_MINUS;
6433       break;
6434
6435     case 0x167e:        // (green steel)
6436       element = EL_STEEL_CHAR_APOSTROPHE;
6437       break;
6438
6439     case 0x167f:        // (green steel)
6440       element = EL_STEEL_CHAR_PARENLEFT;
6441       break;
6442
6443     case 0x1680:        // (green steel)
6444       element = EL_STEEL_CHAR_PARENRIGHT;
6445       break;
6446
6447     case 0x1681:        // gate (red)
6448       element = EL_EM_GATE_1;
6449       break;
6450
6451     case 0x1682:        // secret gate (red)
6452       element = EL_EM_GATE_1_GRAY;
6453       break;
6454
6455     case 0x1683:        // gate (yellow)
6456       element = EL_EM_GATE_2;
6457       break;
6458
6459     case 0x1684:        // secret gate (yellow)
6460       element = EL_EM_GATE_2_GRAY;
6461       break;
6462
6463     case 0x1685:        // gate (blue)
6464       element = EL_EM_GATE_4;
6465       break;
6466
6467     case 0x1686:        // secret gate (blue)
6468       element = EL_EM_GATE_4_GRAY;
6469       break;
6470
6471     case 0x1687:        // gate (green)
6472       element = EL_EM_GATE_3;
6473       break;
6474
6475     case 0x1688:        // secret gate (green)
6476       element = EL_EM_GATE_3_GRAY;
6477       break;
6478
6479     case 0x1689:        // gate (white)
6480       element = EL_DC_GATE_WHITE;
6481       break;
6482
6483     case 0x168a:        // secret gate (white)
6484       element = EL_DC_GATE_WHITE_GRAY;
6485       break;
6486
6487     case 0x168b:        // secret gate (no key)
6488       element = EL_DC_GATE_FAKE_GRAY;
6489       break;
6490
6491     case 0x168c:
6492       element = EL_ROBOT_WHEEL;
6493       break;
6494
6495     case 0x168d:
6496       element = EL_DC_TIMEGATE_SWITCH;
6497       break;
6498
6499     case 0x168e:
6500       element = EL_ACID_POOL_BOTTOM;
6501       break;
6502
6503     case 0x168f:
6504       element = EL_ACID_POOL_TOPLEFT;
6505       break;
6506
6507     case 0x1690:
6508       element = EL_ACID_POOL_TOPRIGHT;
6509       break;
6510
6511     case 0x1691:
6512       element = EL_ACID_POOL_BOTTOMLEFT;
6513       break;
6514
6515     case 0x1692:
6516       element = EL_ACID_POOL_BOTTOMRIGHT;
6517       break;
6518
6519     case 0x1693:
6520       element = EL_STEELWALL;
6521       break;
6522
6523     case 0x1694:
6524       element = EL_STEELWALL_SLIPPERY;
6525       break;
6526
6527     case 0x1695:        // steel wall (not round)
6528       element = EL_STEELWALL;
6529       break;
6530
6531     case 0x1696:        // steel wall (left)
6532       element = EL_DC_STEELWALL_1_LEFT;
6533       break;
6534
6535     case 0x1697:        // steel wall (bottom)
6536       element = EL_DC_STEELWALL_1_BOTTOM;
6537       break;
6538
6539     case 0x1698:        // steel wall (right)
6540       element = EL_DC_STEELWALL_1_RIGHT;
6541       break;
6542
6543     case 0x1699:        // steel wall (top)
6544       element = EL_DC_STEELWALL_1_TOP;
6545       break;
6546
6547     case 0x169a:        // steel wall (left/bottom)
6548       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6549       break;
6550
6551     case 0x169b:        // steel wall (right/bottom)
6552       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6553       break;
6554
6555     case 0x169c:        // steel wall (right/top)
6556       element = EL_DC_STEELWALL_1_TOPRIGHT;
6557       break;
6558
6559     case 0x169d:        // steel wall (left/top)
6560       element = EL_DC_STEELWALL_1_TOPLEFT;
6561       break;
6562
6563     case 0x169e:        // steel wall (right/bottom small)
6564       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6565       break;
6566
6567     case 0x169f:        // steel wall (left/bottom small)
6568       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6569       break;
6570
6571     case 0x16a0:        // steel wall (right/top small)
6572       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6573       break;
6574
6575     case 0x16a1:        // steel wall (left/top small)
6576       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6577       break;
6578
6579     case 0x16a2:        // steel wall (left/right)
6580       element = EL_DC_STEELWALL_1_VERTICAL;
6581       break;
6582
6583     case 0x16a3:        // steel wall (top/bottom)
6584       element = EL_DC_STEELWALL_1_HORIZONTAL;
6585       break;
6586
6587     case 0x16a4:        // steel wall 2 (left end)
6588       element = EL_DC_STEELWALL_2_LEFT;
6589       break;
6590
6591     case 0x16a5:        // steel wall 2 (right end)
6592       element = EL_DC_STEELWALL_2_RIGHT;
6593       break;
6594
6595     case 0x16a6:        // steel wall 2 (top end)
6596       element = EL_DC_STEELWALL_2_TOP;
6597       break;
6598
6599     case 0x16a7:        // steel wall 2 (bottom end)
6600       element = EL_DC_STEELWALL_2_BOTTOM;
6601       break;
6602
6603     case 0x16a8:        // steel wall 2 (left/right)
6604       element = EL_DC_STEELWALL_2_HORIZONTAL;
6605       break;
6606
6607     case 0x16a9:        // steel wall 2 (up/down)
6608       element = EL_DC_STEELWALL_2_VERTICAL;
6609       break;
6610
6611     case 0x16aa:        // steel wall 2 (mid)
6612       element = EL_DC_STEELWALL_2_MIDDLE;
6613       break;
6614
6615     case 0x16ab:
6616       element = EL_SIGN_EXCLAMATION;
6617       break;
6618
6619     case 0x16ac:
6620       element = EL_SIGN_RADIOACTIVITY;
6621       break;
6622
6623     case 0x16ad:
6624       element = EL_SIGN_STOP;
6625       break;
6626
6627     case 0x16ae:
6628       element = EL_SIGN_WHEELCHAIR;
6629       break;
6630
6631     case 0x16af:
6632       element = EL_SIGN_PARKING;
6633       break;
6634
6635     case 0x16b0:
6636       element = EL_SIGN_NO_ENTRY;
6637       break;
6638
6639     case 0x16b1:
6640       element = EL_SIGN_HEART;
6641       break;
6642
6643     case 0x16b2:
6644       element = EL_SIGN_GIVE_WAY;
6645       break;
6646
6647     case 0x16b3:
6648       element = EL_SIGN_ENTRY_FORBIDDEN;
6649       break;
6650
6651     case 0x16b4:
6652       element = EL_SIGN_EMERGENCY_EXIT;
6653       break;
6654
6655     case 0x16b5:
6656       element = EL_SIGN_YIN_YANG;
6657       break;
6658
6659     case 0x16b6:
6660       element = EL_WALL_EMERALD;
6661       break;
6662
6663     case 0x16b7:
6664       element = EL_WALL_DIAMOND;
6665       break;
6666
6667     case 0x16b8:
6668       element = EL_WALL_PEARL;
6669       break;
6670
6671     case 0x16b9:
6672       element = EL_WALL_CRYSTAL;
6673       break;
6674
6675     case 0x16ba:
6676       element = EL_INVISIBLE_WALL;
6677       break;
6678
6679     case 0x16bb:
6680       element = EL_INVISIBLE_STEELWALL;
6681       break;
6682
6683       // 0x16bc - 0x16cb:
6684       // EL_INVISIBLE_SAND
6685
6686     case 0x16cc:
6687       element = EL_LIGHT_SWITCH;
6688       break;
6689
6690     case 0x16cd:
6691       element = EL_ENVELOPE_1;
6692       break;
6693
6694     default:
6695       if (element >= 0x0117 && element <= 0x036e)       // (?)
6696         element = EL_DIAMOND;
6697       else if (element >= 0x042d && element <= 0x0684)  // (?)
6698         element = EL_EMERALD;
6699       else if (element >= 0x157c && element <= 0x158b)
6700         element = EL_SAND;
6701       else if (element >= 0x1590 && element <= 0x159f)
6702         element = EL_DC_LANDMINE;
6703       else if (element >= 0x16bc && element <= 0x16cb)
6704         element = EL_INVISIBLE_SAND;
6705       else
6706       {
6707         Warn("unknown Diamond Caves element 0x%04x", element);
6708
6709         element = EL_UNKNOWN;
6710       }
6711       break;
6712   }
6713
6714   return getMappedElement(element);
6715 }
6716
6717 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6718 {
6719   byte header[DC_LEVEL_HEADER_SIZE];
6720   int envelope_size;
6721   int envelope_header_pos = 62;
6722   int envelope_content_pos = 94;
6723   int level_name_pos = 251;
6724   int level_author_pos = 292;
6725   int envelope_header_len;
6726   int envelope_content_len;
6727   int level_name_len;
6728   int level_author_len;
6729   int fieldx, fieldy;
6730   int num_yamyam_contents;
6731   int i, x, y;
6732
6733   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6734
6735   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6736   {
6737     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6738
6739     header[i * 2 + 0] = header_word >> 8;
6740     header[i * 2 + 1] = header_word & 0xff;
6741   }
6742
6743   // read some values from level header to check level decoding integrity
6744   fieldx = header[6] | (header[7] << 8);
6745   fieldy = header[8] | (header[9] << 8);
6746   num_yamyam_contents = header[60] | (header[61] << 8);
6747
6748   // do some simple sanity checks to ensure that level was correctly decoded
6749   if (fieldx < 1 || fieldx > 256 ||
6750       fieldy < 1 || fieldy > 256 ||
6751       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6752   {
6753     level->no_valid_file = TRUE;
6754
6755     Warn("cannot decode level from stream -- using empty level");
6756
6757     return;
6758   }
6759
6760   // maximum envelope header size is 31 bytes
6761   envelope_header_len   = header[envelope_header_pos];
6762   // maximum envelope content size is 110 (156?) bytes
6763   envelope_content_len  = header[envelope_content_pos];
6764
6765   // maximum level title size is 40 bytes
6766   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6767   // maximum level author size is 30 (51?) bytes
6768   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6769
6770   envelope_size = 0;
6771
6772   for (i = 0; i < envelope_header_len; i++)
6773     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6774       level->envelope[0].text[envelope_size++] =
6775         header[envelope_header_pos + 1 + i];
6776
6777   if (envelope_header_len > 0 && envelope_content_len > 0)
6778   {
6779     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6780       level->envelope[0].text[envelope_size++] = '\n';
6781     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6782       level->envelope[0].text[envelope_size++] = '\n';
6783   }
6784
6785   for (i = 0; i < envelope_content_len; i++)
6786     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6787       level->envelope[0].text[envelope_size++] =
6788         header[envelope_content_pos + 1 + i];
6789
6790   level->envelope[0].text[envelope_size] = '\0';
6791
6792   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6793   level->envelope[0].ysize = 10;
6794   level->envelope[0].autowrap = TRUE;
6795   level->envelope[0].centered = TRUE;
6796
6797   for (i = 0; i < level_name_len; i++)
6798     level->name[i] = header[level_name_pos + 1 + i];
6799   level->name[level_name_len] = '\0';
6800
6801   for (i = 0; i < level_author_len; i++)
6802     level->author[i] = header[level_author_pos + 1 + i];
6803   level->author[level_author_len] = '\0';
6804
6805   num_yamyam_contents = header[60] | (header[61] << 8);
6806   level->num_yamyam_contents =
6807     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6808
6809   for (i = 0; i < num_yamyam_contents; i++)
6810   {
6811     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6812     {
6813       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6814       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6815
6816       if (i < MAX_ELEMENT_CONTENTS)
6817         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6818     }
6819   }
6820
6821   fieldx = header[6] | (header[7] << 8);
6822   fieldy = header[8] | (header[9] << 8);
6823   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6824   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6825
6826   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6827   {
6828     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6829     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6830
6831     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6832       level->field[x][y] = getMappedElement_DC(element_dc);
6833   }
6834
6835   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6836   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6837   level->field[x][y] = EL_PLAYER_1;
6838
6839   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6840   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6841   level->field[x][y] = EL_PLAYER_2;
6842
6843   level->gems_needed            = header[18] | (header[19] << 8);
6844
6845   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6846   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6847   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6848   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6849   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6850   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6851   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6852   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6853   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6854   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6855   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6856   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6857
6858   level->time                   = header[44] | (header[45] << 8);
6859
6860   level->amoeba_speed           = header[46] | (header[47] << 8);
6861   level->time_light             = header[48] | (header[49] << 8);
6862   level->time_timegate          = header[50] | (header[51] << 8);
6863   level->time_wheel             = header[52] | (header[53] << 8);
6864   level->time_magic_wall        = header[54] | (header[55] << 8);
6865   level->extra_time             = header[56] | (header[57] << 8);
6866   level->shield_normal_time     = header[58] | (header[59] << 8);
6867
6868   // shield and extra time elements do not have a score
6869   level->score[SC_SHIELD]       = 0;
6870   level->extra_time_score       = 0;
6871
6872   // set time for normal and deadly shields to the same value
6873   level->shield_deadly_time     = level->shield_normal_time;
6874
6875   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6876   // can slip down from flat walls, like normal walls and steel walls
6877   level->em_slippery_gems = TRUE;
6878
6879   // time score is counted for each 10 seconds left in Diamond Caves levels
6880   level->time_score_base = 10;
6881 }
6882
6883 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6884                                      struct LevelFileInfo *level_file_info,
6885                                      boolean level_info_only)
6886 {
6887   char *filename = level_file_info->filename;
6888   File *file;
6889   int num_magic_bytes = 8;
6890   char magic_bytes[num_magic_bytes + 1];
6891   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6892
6893   if (!(file = openFile(filename, MODE_READ)))
6894   {
6895     level->no_valid_file = TRUE;
6896
6897     if (!level_info_only)
6898       Warn("cannot read level '%s' -- using empty level", filename);
6899
6900     return;
6901   }
6902
6903   // fseek(file, 0x0000, SEEK_SET);
6904
6905   if (level_file_info->packed)
6906   {
6907     // read "magic bytes" from start of file
6908     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6909       magic_bytes[0] = '\0';
6910
6911     // check "magic bytes" for correct file format
6912     if (!strPrefix(magic_bytes, "DC2"))
6913     {
6914       level->no_valid_file = TRUE;
6915
6916       Warn("unknown DC level file '%s' -- using empty level", filename);
6917
6918       return;
6919     }
6920
6921     if (strPrefix(magic_bytes, "DC2Win95") ||
6922         strPrefix(magic_bytes, "DC2Win98"))
6923     {
6924       int position_first_level = 0x00fa;
6925       int extra_bytes = 4;
6926       int skip_bytes;
6927
6928       // advance file stream to first level inside the level package
6929       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6930
6931       // each block of level data is followed by block of non-level data
6932       num_levels_to_skip *= 2;
6933
6934       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6935       while (num_levels_to_skip >= 0)
6936       {
6937         // advance file stream to next level inside the level package
6938         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6939         {
6940           level->no_valid_file = TRUE;
6941
6942           Warn("cannot fseek in file '%s' -- using empty level", filename);
6943
6944           return;
6945         }
6946
6947         // skip apparently unused extra bytes following each level
6948         ReadUnusedBytesFromFile(file, extra_bytes);
6949
6950         // read size of next level in level package
6951         skip_bytes = getFile32BitLE(file);
6952
6953         num_levels_to_skip--;
6954       }
6955     }
6956     else
6957     {
6958       level->no_valid_file = TRUE;
6959
6960       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6961
6962       return;
6963     }
6964   }
6965
6966   LoadLevelFromFileStream_DC(file, level);
6967
6968   closeFile(file);
6969 }
6970
6971
6972 // ----------------------------------------------------------------------------
6973 // functions for loading SB level
6974 // ----------------------------------------------------------------------------
6975
6976 int getMappedElement_SB(int element_ascii, boolean use_ces)
6977 {
6978   static struct
6979   {
6980     int ascii;
6981     int sb;
6982     int ce;
6983   }
6984   sb_element_mapping[] =
6985   {
6986     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6987     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6988     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6989     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6990     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6991     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6992     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6993     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6994
6995     { 0,   -1,                      -1          },
6996   };
6997
6998   int i;
6999
7000   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7001     if (element_ascii == sb_element_mapping[i].ascii)
7002       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7003
7004   return EL_UNDEFINED;
7005 }
7006
7007 static void SetLevelSettings_SB(struct LevelInfo *level)
7008 {
7009   // time settings
7010   level->time = 0;
7011   level->use_step_counter = TRUE;
7012
7013   // score settings
7014   level->score[SC_TIME_BONUS] = 0;
7015   level->time_score_base = 1;
7016   level->rate_time_over_score = TRUE;
7017
7018   // game settings
7019   level->auto_exit_sokoban = TRUE;
7020 }
7021
7022 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7023                                      struct LevelFileInfo *level_file_info,
7024                                      boolean level_info_only)
7025 {
7026   char *filename = level_file_info->filename;
7027   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7028   char last_comment[MAX_LINE_LEN];
7029   char level_name[MAX_LINE_LEN];
7030   char *line_ptr;
7031   File *file;
7032   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7033   boolean read_continued_line = FALSE;
7034   boolean reading_playfield = FALSE;
7035   boolean got_valid_playfield_line = FALSE;
7036   boolean invalid_playfield_char = FALSE;
7037   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7038   int file_level_nr = 0;
7039   int x = 0, y = 0;             // initialized to make compilers happy
7040
7041   last_comment[0] = '\0';
7042   level_name[0] = '\0';
7043
7044   if (!(file = openFile(filename, MODE_READ)))
7045   {
7046     level->no_valid_file = TRUE;
7047
7048     if (!level_info_only)
7049       Warn("cannot read level '%s' -- using empty level", filename);
7050
7051     return;
7052   }
7053
7054   while (!checkEndOfFile(file))
7055   {
7056     // level successfully read, but next level may follow here
7057     if (!got_valid_playfield_line && reading_playfield)
7058     {
7059       // read playfield from single level file -- skip remaining file
7060       if (!level_file_info->packed)
7061         break;
7062
7063       if (file_level_nr >= num_levels_to_skip)
7064         break;
7065
7066       file_level_nr++;
7067
7068       last_comment[0] = '\0';
7069       level_name[0] = '\0';
7070
7071       reading_playfield = FALSE;
7072     }
7073
7074     got_valid_playfield_line = FALSE;
7075
7076     // read next line of input file
7077     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7078       break;
7079
7080     // cut trailing line break (this can be newline and/or carriage return)
7081     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7082       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7083         *line_ptr = '\0';
7084
7085     // copy raw input line for later use (mainly debugging output)
7086     strcpy(line_raw, line);
7087
7088     if (read_continued_line)
7089     {
7090       // append new line to existing line, if there is enough space
7091       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7092         strcat(previous_line, line_ptr);
7093
7094       strcpy(line, previous_line);      // copy storage buffer to line
7095
7096       read_continued_line = FALSE;
7097     }
7098
7099     // if the last character is '\', continue at next line
7100     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7101     {
7102       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7103       strcpy(previous_line, line);      // copy line to storage buffer
7104
7105       read_continued_line = TRUE;
7106
7107       continue;
7108     }
7109
7110     // skip empty lines
7111     if (line[0] == '\0')
7112       continue;
7113
7114     // extract comment text from comment line
7115     if (line[0] == ';')
7116     {
7117       for (line_ptr = line; *line_ptr; line_ptr++)
7118         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7119           break;
7120
7121       strcpy(last_comment, line_ptr);
7122
7123       continue;
7124     }
7125
7126     // extract level title text from line containing level title
7127     if (line[0] == '\'')
7128     {
7129       strcpy(level_name, &line[1]);
7130
7131       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7132         level_name[strlen(level_name) - 1] = '\0';
7133
7134       continue;
7135     }
7136
7137     // skip lines containing only spaces (or empty lines)
7138     for (line_ptr = line; *line_ptr; line_ptr++)
7139       if (*line_ptr != ' ')
7140         break;
7141     if (*line_ptr == '\0')
7142       continue;
7143
7144     // at this point, we have found a line containing part of a playfield
7145
7146     got_valid_playfield_line = TRUE;
7147
7148     if (!reading_playfield)
7149     {
7150       reading_playfield = TRUE;
7151       invalid_playfield_char = FALSE;
7152
7153       for (x = 0; x < MAX_LEV_FIELDX; x++)
7154         for (y = 0; y < MAX_LEV_FIELDY; y++)
7155           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7156
7157       level->fieldx = 0;
7158       level->fieldy = 0;
7159
7160       // start with topmost tile row
7161       y = 0;
7162     }
7163
7164     // skip playfield line if larger row than allowed
7165     if (y >= MAX_LEV_FIELDY)
7166       continue;
7167
7168     // start with leftmost tile column
7169     x = 0;
7170
7171     // read playfield elements from line
7172     for (line_ptr = line; *line_ptr; line_ptr++)
7173     {
7174       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7175
7176       // stop parsing playfield line if larger column than allowed
7177       if (x >= MAX_LEV_FIELDX)
7178         break;
7179
7180       if (mapped_sb_element == EL_UNDEFINED)
7181       {
7182         invalid_playfield_char = TRUE;
7183
7184         break;
7185       }
7186
7187       level->field[x][y] = mapped_sb_element;
7188
7189       // continue with next tile column
7190       x++;
7191
7192       level->fieldx = MAX(x, level->fieldx);
7193     }
7194
7195     if (invalid_playfield_char)
7196     {
7197       // if first playfield line, treat invalid lines as comment lines
7198       if (y == 0)
7199         reading_playfield = FALSE;
7200
7201       continue;
7202     }
7203
7204     // continue with next tile row
7205     y++;
7206   }
7207
7208   closeFile(file);
7209
7210   level->fieldy = y;
7211
7212   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7213   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7214
7215   if (!reading_playfield)
7216   {
7217     level->no_valid_file = TRUE;
7218
7219     Warn("cannot read level '%s' -- using empty level", filename);
7220
7221     return;
7222   }
7223
7224   if (*level_name != '\0')
7225   {
7226     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7227     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7228   }
7229   else if (*last_comment != '\0')
7230   {
7231     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7232     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7233   }
7234   else
7235   {
7236     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7237   }
7238
7239   // set all empty fields beyond the border walls to invisible steel wall
7240   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7241   {
7242     if ((x == 0 || x == level->fieldx - 1 ||
7243          y == 0 || y == level->fieldy - 1) &&
7244         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7245       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7246                      level->field, level->fieldx, level->fieldy);
7247   }
7248
7249   // set special level settings for Sokoban levels
7250   SetLevelSettings_SB(level);
7251
7252   if (load_xsb_to_ces)
7253   {
7254     // special global settings can now be set in level template
7255     level->use_custom_template = TRUE;
7256   }
7257 }
7258
7259
7260 // -------------------------------------------------------------------------
7261 // functions for handling native levels
7262 // -------------------------------------------------------------------------
7263
7264 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7265                                      struct LevelFileInfo *level_file_info,
7266                                      boolean level_info_only)
7267 {
7268   int pos = 0;
7269
7270   // determine position of requested level inside level package
7271   if (level_file_info->packed)
7272     pos = level_file_info->nr - leveldir_current->first_level;
7273
7274   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7275     level->no_valid_file = TRUE;
7276 }
7277
7278 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7279                                      struct LevelFileInfo *level_file_info,
7280                                      boolean level_info_only)
7281 {
7282   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7283     level->no_valid_file = TRUE;
7284 }
7285
7286 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7287                                      struct LevelFileInfo *level_file_info,
7288                                      boolean level_info_only)
7289 {
7290   int pos = 0;
7291
7292   // determine position of requested level inside level package
7293   if (level_file_info->packed)
7294     pos = level_file_info->nr - leveldir_current->first_level;
7295
7296   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7297     level->no_valid_file = TRUE;
7298 }
7299
7300 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7301                                      struct LevelFileInfo *level_file_info,
7302                                      boolean level_info_only)
7303 {
7304   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7305     level->no_valid_file = TRUE;
7306 }
7307
7308 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7309 {
7310   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7311     CopyNativeLevel_RND_to_BD(level);
7312   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7313     CopyNativeLevel_RND_to_EM(level);
7314   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7315     CopyNativeLevel_RND_to_SP(level);
7316   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7317     CopyNativeLevel_RND_to_MM(level);
7318 }
7319
7320 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7321 {
7322   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7323     CopyNativeLevel_BD_to_RND(level);
7324   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7325     CopyNativeLevel_EM_to_RND(level);
7326   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7327     CopyNativeLevel_SP_to_RND(level);
7328   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7329     CopyNativeLevel_MM_to_RND(level);
7330 }
7331
7332 void SaveNativeLevel(struct LevelInfo *level)
7333 {
7334   // saving native level files only supported for some game engines
7335   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7336       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7337     return;
7338
7339   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7340                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7341   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7342   char *filename = getLevelFilenameFromBasename(basename);
7343
7344   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7345     return;
7346
7347   boolean success = FALSE;
7348
7349   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7350   {
7351     CopyNativeLevel_RND_to_BD(level);
7352     // CopyNativeTape_RND_to_BD(level);
7353
7354     success = SaveNativeLevel_BD(filename);
7355   }
7356   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7357   {
7358     CopyNativeLevel_RND_to_SP(level);
7359     CopyNativeTape_RND_to_SP(level);
7360
7361     success = SaveNativeLevel_SP(filename);
7362   }
7363
7364   if (success)
7365     Request("Native level file saved!", REQ_CONFIRM);
7366   else
7367     Request("Failed to save native level file!", REQ_CONFIRM);
7368 }
7369
7370
7371 // ----------------------------------------------------------------------------
7372 // functions for loading generic level
7373 // ----------------------------------------------------------------------------
7374
7375 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7376                                   struct LevelFileInfo *level_file_info,
7377                                   boolean level_info_only)
7378 {
7379   // always start with reliable default values
7380   setLevelInfoToDefaults(level, level_info_only, TRUE);
7381
7382   switch (level_file_info->type)
7383   {
7384     case LEVEL_FILE_TYPE_RND:
7385       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7386       break;
7387
7388     case LEVEL_FILE_TYPE_BD:
7389       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7390       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7391       break;
7392
7393     case LEVEL_FILE_TYPE_EM:
7394       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7395       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7396       break;
7397
7398     case LEVEL_FILE_TYPE_SP:
7399       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7400       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7401       break;
7402
7403     case LEVEL_FILE_TYPE_MM:
7404       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7405       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7406       break;
7407
7408     case LEVEL_FILE_TYPE_DC:
7409       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7410       break;
7411
7412     case LEVEL_FILE_TYPE_SB:
7413       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7414       break;
7415
7416     default:
7417       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7418       break;
7419   }
7420
7421   // if level file is invalid, restore level structure to default values
7422   if (level->no_valid_file)
7423     setLevelInfoToDefaults(level, level_info_only, FALSE);
7424
7425   if (check_special_flags("use_native_bd_game_engine"))
7426     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7427
7428   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7429     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7430
7431   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7432     CopyNativeLevel_Native_to_RND(level);
7433 }
7434
7435 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7436 {
7437   static struct LevelFileInfo level_file_info;
7438
7439   // always start with reliable default values
7440   setFileInfoToDefaults(&level_file_info);
7441
7442   level_file_info.nr = 0;                       // unknown level number
7443   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7444
7445   setString(&level_file_info.filename, filename);
7446
7447   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7448 }
7449
7450 static void LoadLevel_InitVersion(struct LevelInfo *level)
7451 {
7452   int i, j;
7453
7454   if (leveldir_current == NULL)         // only when dumping level
7455     return;
7456
7457   // all engine modifications also valid for levels which use latest engine
7458   if (level->game_version < VERSION_IDENT(3,2,0,5))
7459   {
7460     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7461     level->time_score_base = 10;
7462   }
7463
7464   if (leveldir_current->latest_engine)
7465   {
7466     // ---------- use latest game engine --------------------------------------
7467
7468     /* For all levels which are forced to use the latest game engine version
7469        (normally all but user contributed, private and undefined levels), set
7470        the game engine version to the actual version; this allows for actual
7471        corrections in the game engine to take effect for existing, converted
7472        levels (from "classic" or other existing games) to make the emulation
7473        of the corresponding game more accurate, while (hopefully) not breaking
7474        existing levels created from other players. */
7475
7476     level->game_version = GAME_VERSION_ACTUAL;
7477
7478     /* Set special EM style gems behaviour: EM style gems slip down from
7479        normal, steel and growing wall. As this is a more fundamental change,
7480        it seems better to set the default behaviour to "off" (as it is more
7481        natural) and make it configurable in the level editor (as a property
7482        of gem style elements). Already existing converted levels (neither
7483        private nor contributed levels) are changed to the new behaviour. */
7484
7485     if (level->file_version < FILE_VERSION_2_0)
7486       level->em_slippery_gems = TRUE;
7487
7488     return;
7489   }
7490
7491   // ---------- use game engine the level was created with --------------------
7492
7493   /* For all levels which are not forced to use the latest game engine
7494      version (normally user contributed, private and undefined levels),
7495      use the version of the game engine the levels were created for.
7496
7497      Since 2.0.1, the game engine version is now directly stored
7498      in the level file (chunk "VERS"), so there is no need anymore
7499      to set the game version from the file version (except for old,
7500      pre-2.0 levels, where the game version is still taken from the
7501      file format version used to store the level -- see above). */
7502
7503   // player was faster than enemies in 1.0.0 and before
7504   if (level->file_version == FILE_VERSION_1_0)
7505     for (i = 0; i < MAX_PLAYERS; i++)
7506       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7507
7508   // default behaviour for EM style gems was "slippery" only in 2.0.1
7509   if (level->game_version == VERSION_IDENT(2,0,1,0))
7510     level->em_slippery_gems = TRUE;
7511
7512   // springs could be pushed over pits before (pre-release version) 2.2.0
7513   if (level->game_version < VERSION_IDENT(2,2,0,0))
7514     level->use_spring_bug = TRUE;
7515
7516   if (level->game_version < VERSION_IDENT(3,2,0,5))
7517   {
7518     // time orb caused limited time in endless time levels before 3.2.0-5
7519     level->use_time_orb_bug = TRUE;
7520
7521     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7522     level->block_snap_field = FALSE;
7523
7524     // extra time score was same value as time left score before 3.2.0-5
7525     level->extra_time_score = level->score[SC_TIME_BONUS];
7526   }
7527
7528   if (level->game_version < VERSION_IDENT(3,2,0,7))
7529   {
7530     // default behaviour for snapping was "not continuous" before 3.2.0-7
7531     level->continuous_snapping = FALSE;
7532   }
7533
7534   // only few elements were able to actively move into acid before 3.1.0
7535   // trigger settings did not exist before 3.1.0; set to default "any"
7536   if (level->game_version < VERSION_IDENT(3,1,0,0))
7537   {
7538     // correct "can move into acid" settings (all zero in old levels)
7539
7540     level->can_move_into_acid_bits = 0; // nothing can move into acid
7541     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7542
7543     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7544     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7545     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7546     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7547
7548     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7549       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7550
7551     // correct trigger settings (stored as zero == "none" in old levels)
7552
7553     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7554     {
7555       int element = EL_CUSTOM_START + i;
7556       struct ElementInfo *ei = &element_info[element];
7557
7558       for (j = 0; j < ei->num_change_pages; j++)
7559       {
7560         struct ElementChangeInfo *change = &ei->change_page[j];
7561
7562         change->trigger_player = CH_PLAYER_ANY;
7563         change->trigger_page = CH_PAGE_ANY;
7564       }
7565     }
7566   }
7567
7568   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7569   {
7570     int element = EL_CUSTOM_256;
7571     struct ElementInfo *ei = &element_info[element];
7572     struct ElementChangeInfo *change = &ei->change_page[0];
7573
7574     /* This is needed to fix a problem that was caused by a bugfix in function
7575        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7576        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7577        not replace walkable elements, but instead just placed the player on it,
7578        without placing the Sokoban field under the player). Unfortunately, this
7579        breaks "Snake Bite" style levels when the snake is halfway through a door
7580        that just closes (the snake head is still alive and can be moved in this
7581        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7582        player (without Sokoban element) which then gets killed as designed). */
7583
7584     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7585          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7586         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7587       change->target_element = EL_PLAYER_1;
7588   }
7589
7590   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7591   if (level->game_version < VERSION_IDENT(3,2,5,0))
7592   {
7593     /* This is needed to fix a problem that was caused by a bugfix in function
7594        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7595        corrects the behaviour when a custom element changes to another custom
7596        element with a higher element number that has change actions defined.
7597        Normally, only one change per frame is allowed for custom elements.
7598        Therefore, it is checked if a custom element already changed in the
7599        current frame; if it did, subsequent changes are suppressed.
7600        Unfortunately, this is only checked for element changes, but not for
7601        change actions, which are still executed. As the function above loops
7602        through all custom elements from lower to higher, an element change
7603        resulting in a lower CE number won't be checked again, while a target
7604        element with a higher number will also be checked, and potential change
7605        actions will get executed for this CE, too (which is wrong), while
7606        further changes are ignored (which is correct). As this bugfix breaks
7607        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7608        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7609        behaviour for existing levels and tapes that make use of this bug */
7610
7611     level->use_action_after_change_bug = TRUE;
7612   }
7613
7614   // not centering level after relocating player was default only in 3.2.3
7615   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7616     level->shifted_relocation = TRUE;
7617
7618   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7619   if (level->game_version < VERSION_IDENT(3,2,6,0))
7620     level->em_explodes_by_fire = TRUE;
7621
7622   // levels were solved by the first player entering an exit up to 4.1.0.0
7623   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7624     level->solved_by_one_player = TRUE;
7625
7626   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7627   if (level->game_version < VERSION_IDENT(4,1,1,1))
7628     level->use_life_bugs = TRUE;
7629
7630   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7631   if (level->game_version < VERSION_IDENT(4,1,1,1))
7632     level->sb_objects_needed = FALSE;
7633
7634   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7635   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7636     level->finish_dig_collect = FALSE;
7637
7638   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7639   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7640     level->keep_walkable_ce = TRUE;
7641 }
7642
7643 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7644 {
7645   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7646   int x, y;
7647
7648   // check if this level is (not) a Sokoban level
7649   for (y = 0; y < level->fieldy; y++)
7650     for (x = 0; x < level->fieldx; x++)
7651       if (!IS_SB_ELEMENT(Tile[x][y]))
7652         is_sokoban_level = FALSE;
7653
7654   if (is_sokoban_level)
7655   {
7656     // set special level settings for Sokoban levels
7657     SetLevelSettings_SB(level);
7658   }
7659 }
7660
7661 static void LoadLevel_InitSettings(struct LevelInfo *level)
7662 {
7663   // adjust level settings for (non-native) Sokoban-style levels
7664   LoadLevel_InitSettings_SB(level);
7665
7666   // rename levels with title "nameless level" or if renaming is forced
7667   if (leveldir_current->empty_level_name != NULL &&
7668       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7669        leveldir_current->force_level_name))
7670     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7671              leveldir_current->empty_level_name, level_nr);
7672 }
7673
7674 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7675 {
7676   int i, x, y;
7677
7678   // map elements that have changed in newer versions
7679   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7680                                                     level->game_version);
7681   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7682     for (x = 0; x < 3; x++)
7683       for (y = 0; y < 3; y++)
7684         level->yamyam_content[i].e[x][y] =
7685           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7686                                     level->game_version);
7687
7688 }
7689
7690 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7691 {
7692   int i, j;
7693
7694   // map custom element change events that have changed in newer versions
7695   // (these following values were accidentally changed in version 3.0.1)
7696   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7697   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7698   {
7699     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7700     {
7701       int element = EL_CUSTOM_START + i;
7702
7703       // order of checking and copying events to be mapped is important
7704       // (do not change the start and end value -- they are constant)
7705       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7706       {
7707         if (HAS_CHANGE_EVENT(element, j - 2))
7708         {
7709           SET_CHANGE_EVENT(element, j - 2, FALSE);
7710           SET_CHANGE_EVENT(element, j, TRUE);
7711         }
7712       }
7713
7714       // order of checking and copying events to be mapped is important
7715       // (do not change the start and end value -- they are constant)
7716       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7717       {
7718         if (HAS_CHANGE_EVENT(element, j - 1))
7719         {
7720           SET_CHANGE_EVENT(element, j - 1, FALSE);
7721           SET_CHANGE_EVENT(element, j, TRUE);
7722         }
7723       }
7724     }
7725   }
7726
7727   // initialize "can_change" field for old levels with only one change page
7728   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7729   {
7730     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7731     {
7732       int element = EL_CUSTOM_START + i;
7733
7734       if (CAN_CHANGE(element))
7735         element_info[element].change->can_change = TRUE;
7736     }
7737   }
7738
7739   // correct custom element values (for old levels without these options)
7740   if (level->game_version < VERSION_IDENT(3,1,1,0))
7741   {
7742     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7743     {
7744       int element = EL_CUSTOM_START + i;
7745       struct ElementInfo *ei = &element_info[element];
7746
7747       if (ei->access_direction == MV_NO_DIRECTION)
7748         ei->access_direction = MV_ALL_DIRECTIONS;
7749     }
7750   }
7751
7752   // correct custom element values (fix invalid values for all versions)
7753   if (1)
7754   {
7755     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7756     {
7757       int element = EL_CUSTOM_START + i;
7758       struct ElementInfo *ei = &element_info[element];
7759
7760       for (j = 0; j < ei->num_change_pages; j++)
7761       {
7762         struct ElementChangeInfo *change = &ei->change_page[j];
7763
7764         if (change->trigger_player == CH_PLAYER_NONE)
7765           change->trigger_player = CH_PLAYER_ANY;
7766
7767         if (change->trigger_side == CH_SIDE_NONE)
7768           change->trigger_side = CH_SIDE_ANY;
7769       }
7770     }
7771   }
7772
7773   // initialize "can_explode" field for old levels which did not store this
7774   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7775   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7776   {
7777     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7778     {
7779       int element = EL_CUSTOM_START + i;
7780
7781       if (EXPLODES_1X1_OLD(element))
7782         element_info[element].explosion_type = EXPLODES_1X1;
7783
7784       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7785                                              EXPLODES_SMASHED(element) ||
7786                                              EXPLODES_IMPACT(element)));
7787     }
7788   }
7789
7790   // correct previously hard-coded move delay values for maze runner style
7791   if (level->game_version < VERSION_IDENT(3,1,1,0))
7792   {
7793     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7794     {
7795       int element = EL_CUSTOM_START + i;
7796
7797       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7798       {
7799         // previously hard-coded and therefore ignored
7800         element_info[element].move_delay_fixed = 9;
7801         element_info[element].move_delay_random = 0;
7802       }
7803     }
7804   }
7805
7806   // set some other uninitialized values of custom elements in older levels
7807   if (level->game_version < VERSION_IDENT(3,1,0,0))
7808   {
7809     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7810     {
7811       int element = EL_CUSTOM_START + i;
7812
7813       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7814
7815       element_info[element].explosion_delay = 17;
7816       element_info[element].ignition_delay = 8;
7817     }
7818   }
7819
7820   // set mouse click change events to work for left/middle/right mouse button
7821   if (level->game_version < VERSION_IDENT(4,2,3,0))
7822   {
7823     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7824     {
7825       int element = EL_CUSTOM_START + i;
7826       struct ElementInfo *ei = &element_info[element];
7827
7828       for (j = 0; j < ei->num_change_pages; j++)
7829       {
7830         struct ElementChangeInfo *change = &ei->change_page[j];
7831
7832         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7833             change->has_event[CE_PRESSED_BY_MOUSE] ||
7834             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7835             change->has_event[CE_MOUSE_PRESSED_ON_X])
7836           change->trigger_side = CH_SIDE_ANY;
7837       }
7838     }
7839   }
7840 }
7841
7842 static void LoadLevel_InitElements(struct LevelInfo *level)
7843 {
7844   LoadLevel_InitStandardElements(level);
7845
7846   if (level->file_has_custom_elements)
7847     LoadLevel_InitCustomElements(level);
7848
7849   // initialize element properties for level editor etc.
7850   InitElementPropertiesEngine(level->game_version);
7851   InitElementPropertiesGfxElement();
7852 }
7853
7854 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7855 {
7856   int x, y;
7857
7858   // map elements that have changed in newer versions
7859   for (y = 0; y < level->fieldy; y++)
7860     for (x = 0; x < level->fieldx; x++)
7861       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7862                                                      level->game_version);
7863
7864   // clear unused playfield data (nicer if level gets resized in editor)
7865   for (x = 0; x < MAX_LEV_FIELDX; x++)
7866     for (y = 0; y < MAX_LEV_FIELDY; y++)
7867       if (x >= level->fieldx || y >= level->fieldy)
7868         level->field[x][y] = EL_EMPTY;
7869
7870   // copy elements to runtime playfield array
7871   for (x = 0; x < MAX_LEV_FIELDX; x++)
7872     for (y = 0; y < MAX_LEV_FIELDY; y++)
7873       Tile[x][y] = level->field[x][y];
7874
7875   // initialize level size variables for faster access
7876   lev_fieldx = level->fieldx;
7877   lev_fieldy = level->fieldy;
7878
7879   // determine border element for this level
7880   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7881     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7882   else
7883     SetBorderElement();
7884 }
7885
7886 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7887 {
7888   struct LevelFileInfo *level_file_info = &level->file_info;
7889
7890   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7891     CopyNativeLevel_RND_to_Native(level);
7892 }
7893
7894 static void LoadLevelTemplate_LoadAndInit(void)
7895 {
7896   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7897
7898   LoadLevel_InitVersion(&level_template);
7899   LoadLevel_InitElements(&level_template);
7900   LoadLevel_InitSettings(&level_template);
7901
7902   ActivateLevelTemplate();
7903 }
7904
7905 void LoadLevelTemplate(int nr)
7906 {
7907   if (!fileExists(getGlobalLevelTemplateFilename()))
7908   {
7909     Warn("no level template found for this level");
7910
7911     return;
7912   }
7913
7914   setLevelFileInfo(&level_template.file_info, nr);
7915
7916   LoadLevelTemplate_LoadAndInit();
7917 }
7918
7919 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7920 {
7921   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7922
7923   LoadLevelTemplate_LoadAndInit();
7924 }
7925
7926 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7927 {
7928   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7929
7930   if (level.use_custom_template)
7931   {
7932     if (network_level != NULL)
7933       LoadNetworkLevelTemplate(network_level);
7934     else
7935       LoadLevelTemplate(-1);
7936   }
7937
7938   LoadLevel_InitVersion(&level);
7939   LoadLevel_InitElements(&level);
7940   LoadLevel_InitPlayfield(&level);
7941   LoadLevel_InitSettings(&level);
7942
7943   LoadLevel_InitNativeEngines(&level);
7944 }
7945
7946 void LoadLevel(int nr)
7947 {
7948   SetLevelSetInfo(leveldir_current->identifier, nr);
7949
7950   setLevelFileInfo(&level.file_info, nr);
7951
7952   LoadLevel_LoadAndInit(NULL);
7953 }
7954
7955 void LoadLevelInfoOnly(int nr)
7956 {
7957   setLevelFileInfo(&level.file_info, nr);
7958
7959   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7960 }
7961
7962 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7963 {
7964   SetLevelSetInfo(network_level->leveldir_identifier,
7965                   network_level->file_info.nr);
7966
7967   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7968
7969   LoadLevel_LoadAndInit(network_level);
7970 }
7971
7972 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7973 {
7974   int chunk_size = 0;
7975
7976   chunk_size += putFileVersion(file, level->file_version);
7977   chunk_size += putFileVersion(file, level->game_version);
7978
7979   return chunk_size;
7980 }
7981
7982 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7983 {
7984   int chunk_size = 0;
7985
7986   chunk_size += putFile16BitBE(file, level->creation_date.year);
7987   chunk_size += putFile8Bit(file,    level->creation_date.month);
7988   chunk_size += putFile8Bit(file,    level->creation_date.day);
7989
7990   return chunk_size;
7991 }
7992
7993 #if ENABLE_HISTORIC_CHUNKS
7994 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7995 {
7996   int i, x, y;
7997
7998   putFile8Bit(file, level->fieldx);
7999   putFile8Bit(file, level->fieldy);
8000
8001   putFile16BitBE(file, level->time);
8002   putFile16BitBE(file, level->gems_needed);
8003
8004   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8005     putFile8Bit(file, level->name[i]);
8006
8007   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8008     putFile8Bit(file, level->score[i]);
8009
8010   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8011     for (y = 0; y < 3; y++)
8012       for (x = 0; x < 3; x++)
8013         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8014                            level->yamyam_content[i].e[x][y]));
8015   putFile8Bit(file, level->amoeba_speed);
8016   putFile8Bit(file, level->time_magic_wall);
8017   putFile8Bit(file, level->time_wheel);
8018   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8019                      level->amoeba_content));
8020   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8021   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8022   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8023   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8024
8025   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8026
8027   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8028   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8029   putFile32BitBE(file, level->can_move_into_acid_bits);
8030   putFile8Bit(file, level->dont_collide_with_bits);
8031
8032   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8033   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8034
8035   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8036   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8037   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8038
8039   putFile8Bit(file, level->game_engine_type);
8040
8041   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8042 }
8043 #endif
8044
8045 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8046 {
8047   int chunk_size = 0;
8048   int i;
8049
8050   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8051     chunk_size += putFile8Bit(file, level->name[i]);
8052
8053   return chunk_size;
8054 }
8055
8056 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8057 {
8058   int chunk_size = 0;
8059   int i;
8060
8061   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8062     chunk_size += putFile8Bit(file, level->author[i]);
8063
8064   return chunk_size;
8065 }
8066
8067 #if ENABLE_HISTORIC_CHUNKS
8068 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8069 {
8070   int chunk_size = 0;
8071   int x, y;
8072
8073   for (y = 0; y < level->fieldy; y++)
8074     for (x = 0; x < level->fieldx; x++)
8075       if (level->encoding_16bit_field)
8076         chunk_size += putFile16BitBE(file, level->field[x][y]);
8077       else
8078         chunk_size += putFile8Bit(file, level->field[x][y]);
8079
8080   return chunk_size;
8081 }
8082 #endif
8083
8084 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8085 {
8086   int chunk_size = 0;
8087   int x, y;
8088
8089   for (y = 0; y < level->fieldy; y++) 
8090     for (x = 0; x < level->fieldx; x++) 
8091       chunk_size += putFile16BitBE(file, level->field[x][y]);
8092
8093   return chunk_size;
8094 }
8095
8096 #if ENABLE_HISTORIC_CHUNKS
8097 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8098 {
8099   int i, x, y;
8100
8101   putFile8Bit(file, EL_YAMYAM);
8102   putFile8Bit(file, level->num_yamyam_contents);
8103   putFile8Bit(file, 0);
8104   putFile8Bit(file, 0);
8105
8106   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8107     for (y = 0; y < 3; y++)
8108       for (x = 0; x < 3; x++)
8109         if (level->encoding_16bit_field)
8110           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8111         else
8112           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8113 }
8114 #endif
8115
8116 #if ENABLE_HISTORIC_CHUNKS
8117 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8118 {
8119   int i, x, y;
8120   int num_contents, content_xsize, content_ysize;
8121   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8122
8123   if (element == EL_YAMYAM)
8124   {
8125     num_contents = level->num_yamyam_contents;
8126     content_xsize = 3;
8127     content_ysize = 3;
8128
8129     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8130       for (y = 0; y < 3; y++)
8131         for (x = 0; x < 3; x++)
8132           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8133   }
8134   else if (element == EL_BD_AMOEBA)
8135   {
8136     num_contents = 1;
8137     content_xsize = 1;
8138     content_ysize = 1;
8139
8140     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8141       for (y = 0; y < 3; y++)
8142         for (x = 0; x < 3; x++)
8143           content_array[i][x][y] = EL_EMPTY;
8144     content_array[0][0][0] = level->amoeba_content;
8145   }
8146   else
8147   {
8148     // chunk header already written -- write empty chunk data
8149     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8150
8151     Warn("cannot save content for element '%d'", element);
8152
8153     return;
8154   }
8155
8156   putFile16BitBE(file, element);
8157   putFile8Bit(file, num_contents);
8158   putFile8Bit(file, content_xsize);
8159   putFile8Bit(file, content_ysize);
8160
8161   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8162
8163   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8164     for (y = 0; y < 3; y++)
8165       for (x = 0; x < 3; x++)
8166         putFile16BitBE(file, content_array[i][x][y]);
8167 }
8168 #endif
8169
8170 #if ENABLE_HISTORIC_CHUNKS
8171 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8172 {
8173   int envelope_nr = element - EL_ENVELOPE_1;
8174   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8175   int chunk_size = 0;
8176   int i;
8177
8178   chunk_size += putFile16BitBE(file, element);
8179   chunk_size += putFile16BitBE(file, envelope_len);
8180   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8181   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8182
8183   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8184   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8185
8186   for (i = 0; i < envelope_len; i++)
8187     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8188
8189   return chunk_size;
8190 }
8191 #endif
8192
8193 #if ENABLE_HISTORIC_CHUNKS
8194 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8195                            int num_changed_custom_elements)
8196 {
8197   int i, check = 0;
8198
8199   putFile16BitBE(file, num_changed_custom_elements);
8200
8201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8202   {
8203     int element = EL_CUSTOM_START + i;
8204
8205     struct ElementInfo *ei = &element_info[element];
8206
8207     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8208     {
8209       if (check < num_changed_custom_elements)
8210       {
8211         putFile16BitBE(file, element);
8212         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8213       }
8214
8215       check++;
8216     }
8217   }
8218
8219   if (check != num_changed_custom_elements)     // should not happen
8220     Warn("inconsistent number of custom element properties");
8221 }
8222 #endif
8223
8224 #if ENABLE_HISTORIC_CHUNKS
8225 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8226                            int num_changed_custom_elements)
8227 {
8228   int i, check = 0;
8229
8230   putFile16BitBE(file, num_changed_custom_elements);
8231
8232   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8233   {
8234     int element = EL_CUSTOM_START + i;
8235
8236     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8237     {
8238       if (check < num_changed_custom_elements)
8239       {
8240         putFile16BitBE(file, element);
8241         putFile16BitBE(file, element_info[element].change->target_element);
8242       }
8243
8244       check++;
8245     }
8246   }
8247
8248   if (check != num_changed_custom_elements)     // should not happen
8249     Warn("inconsistent number of custom target elements");
8250 }
8251 #endif
8252
8253 #if ENABLE_HISTORIC_CHUNKS
8254 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8255                            int num_changed_custom_elements)
8256 {
8257   int i, j, x, y, check = 0;
8258
8259   putFile16BitBE(file, num_changed_custom_elements);
8260
8261   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8262   {
8263     int element = EL_CUSTOM_START + i;
8264     struct ElementInfo *ei = &element_info[element];
8265
8266     if (ei->modified_settings)
8267     {
8268       if (check < num_changed_custom_elements)
8269       {
8270         putFile16BitBE(file, element);
8271
8272         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8273           putFile8Bit(file, ei->description[j]);
8274
8275         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8276
8277         // some free bytes for future properties and padding
8278         WriteUnusedBytesToFile(file, 7);
8279
8280         putFile8Bit(file, ei->use_gfx_element);
8281         putFile16BitBE(file, ei->gfx_element_initial);
8282
8283         putFile8Bit(file, ei->collect_score_initial);
8284         putFile8Bit(file, ei->collect_count_initial);
8285
8286         putFile16BitBE(file, ei->push_delay_fixed);
8287         putFile16BitBE(file, ei->push_delay_random);
8288         putFile16BitBE(file, ei->move_delay_fixed);
8289         putFile16BitBE(file, ei->move_delay_random);
8290
8291         putFile16BitBE(file, ei->move_pattern);
8292         putFile8Bit(file, ei->move_direction_initial);
8293         putFile8Bit(file, ei->move_stepsize);
8294
8295         for (y = 0; y < 3; y++)
8296           for (x = 0; x < 3; x++)
8297             putFile16BitBE(file, ei->content.e[x][y]);
8298
8299         putFile32BitBE(file, ei->change->events);
8300
8301         putFile16BitBE(file, ei->change->target_element);
8302
8303         putFile16BitBE(file, ei->change->delay_fixed);
8304         putFile16BitBE(file, ei->change->delay_random);
8305         putFile16BitBE(file, ei->change->delay_frames);
8306
8307         putFile16BitBE(file, ei->change->initial_trigger_element);
8308
8309         putFile8Bit(file, ei->change->explode);
8310         putFile8Bit(file, ei->change->use_target_content);
8311         putFile8Bit(file, ei->change->only_if_complete);
8312         putFile8Bit(file, ei->change->use_random_replace);
8313
8314         putFile8Bit(file, ei->change->random_percentage);
8315         putFile8Bit(file, ei->change->replace_when);
8316
8317         for (y = 0; y < 3; y++)
8318           for (x = 0; x < 3; x++)
8319             putFile16BitBE(file, ei->change->content.e[x][y]);
8320
8321         putFile8Bit(file, ei->slippery_type);
8322
8323         // some free bytes for future properties and padding
8324         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8325       }
8326
8327       check++;
8328     }
8329   }
8330
8331   if (check != num_changed_custom_elements)     // should not happen
8332     Warn("inconsistent number of custom element properties");
8333 }
8334 #endif
8335
8336 #if ENABLE_HISTORIC_CHUNKS
8337 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8338 {
8339   struct ElementInfo *ei = &element_info[element];
8340   int i, j, x, y;
8341
8342   // ---------- custom element base property values (96 bytes) ----------------
8343
8344   putFile16BitBE(file, element);
8345
8346   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8347     putFile8Bit(file, ei->description[i]);
8348
8349   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8350
8351   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8352
8353   putFile8Bit(file, ei->num_change_pages);
8354
8355   putFile16BitBE(file, ei->ce_value_fixed_initial);
8356   putFile16BitBE(file, ei->ce_value_random_initial);
8357   putFile8Bit(file, ei->use_last_ce_value);
8358
8359   putFile8Bit(file, ei->use_gfx_element);
8360   putFile16BitBE(file, ei->gfx_element_initial);
8361
8362   putFile8Bit(file, ei->collect_score_initial);
8363   putFile8Bit(file, ei->collect_count_initial);
8364
8365   putFile8Bit(file, ei->drop_delay_fixed);
8366   putFile8Bit(file, ei->push_delay_fixed);
8367   putFile8Bit(file, ei->drop_delay_random);
8368   putFile8Bit(file, ei->push_delay_random);
8369   putFile16BitBE(file, ei->move_delay_fixed);
8370   putFile16BitBE(file, ei->move_delay_random);
8371
8372   // bits 0 - 15 of "move_pattern" ...
8373   putFile16BitBE(file, ei->move_pattern & 0xffff);
8374   putFile8Bit(file, ei->move_direction_initial);
8375   putFile8Bit(file, ei->move_stepsize);
8376
8377   putFile8Bit(file, ei->slippery_type);
8378
8379   for (y = 0; y < 3; y++)
8380     for (x = 0; x < 3; x++)
8381       putFile16BitBE(file, ei->content.e[x][y]);
8382
8383   putFile16BitBE(file, ei->move_enter_element);
8384   putFile16BitBE(file, ei->move_leave_element);
8385   putFile8Bit(file, ei->move_leave_type);
8386
8387   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8388   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8389
8390   putFile8Bit(file, ei->access_direction);
8391
8392   putFile8Bit(file, ei->explosion_delay);
8393   putFile8Bit(file, ei->ignition_delay);
8394   putFile8Bit(file, ei->explosion_type);
8395
8396   // some free bytes for future custom property values and padding
8397   WriteUnusedBytesToFile(file, 1);
8398
8399   // ---------- change page property values (48 bytes) ------------------------
8400
8401   for (i = 0; i < ei->num_change_pages; i++)
8402   {
8403     struct ElementChangeInfo *change = &ei->change_page[i];
8404     unsigned int event_bits;
8405
8406     // bits 0 - 31 of "has_event[]" ...
8407     event_bits = 0;
8408     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8409       if (change->has_event[j])
8410         event_bits |= (1u << j);
8411     putFile32BitBE(file, event_bits);
8412
8413     putFile16BitBE(file, change->target_element);
8414
8415     putFile16BitBE(file, change->delay_fixed);
8416     putFile16BitBE(file, change->delay_random);
8417     putFile16BitBE(file, change->delay_frames);
8418
8419     putFile16BitBE(file, change->initial_trigger_element);
8420
8421     putFile8Bit(file, change->explode);
8422     putFile8Bit(file, change->use_target_content);
8423     putFile8Bit(file, change->only_if_complete);
8424     putFile8Bit(file, change->use_random_replace);
8425
8426     putFile8Bit(file, change->random_percentage);
8427     putFile8Bit(file, change->replace_when);
8428
8429     for (y = 0; y < 3; y++)
8430       for (x = 0; x < 3; x++)
8431         putFile16BitBE(file, change->target_content.e[x][y]);
8432
8433     putFile8Bit(file, change->can_change);
8434
8435     putFile8Bit(file, change->trigger_side);
8436
8437     putFile8Bit(file, change->trigger_player);
8438     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8439                        log_2(change->trigger_page)));
8440
8441     putFile8Bit(file, change->has_action);
8442     putFile8Bit(file, change->action_type);
8443     putFile8Bit(file, change->action_mode);
8444     putFile16BitBE(file, change->action_arg);
8445
8446     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8447     event_bits = 0;
8448     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8449       if (change->has_event[j])
8450         event_bits |= (1u << (j - 32));
8451     putFile8Bit(file, event_bits);
8452   }
8453 }
8454 #endif
8455
8456 #if ENABLE_HISTORIC_CHUNKS
8457 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8458 {
8459   struct ElementInfo *ei = &element_info[element];
8460   struct ElementGroupInfo *group = ei->group;
8461   int i;
8462
8463   putFile16BitBE(file, element);
8464
8465   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8466     putFile8Bit(file, ei->description[i]);
8467
8468   putFile8Bit(file, group->num_elements);
8469
8470   putFile8Bit(file, ei->use_gfx_element);
8471   putFile16BitBE(file, ei->gfx_element_initial);
8472
8473   putFile8Bit(file, group->choice_mode);
8474
8475   // some free bytes for future values and padding
8476   WriteUnusedBytesToFile(file, 3);
8477
8478   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8479     putFile16BitBE(file, group->element[i]);
8480 }
8481 #endif
8482
8483 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8484                                 boolean write_element)
8485 {
8486   int save_type = entry->save_type;
8487   int data_type = entry->data_type;
8488   int conf_type = entry->conf_type;
8489   int byte_mask = conf_type & CONF_MASK_BYTES;
8490   int element = entry->element;
8491   int default_value = entry->default_value;
8492   int num_bytes = 0;
8493   boolean modified = FALSE;
8494
8495   if (byte_mask != CONF_MASK_MULTI_BYTES)
8496   {
8497     void *value_ptr = entry->value;
8498     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8499                  *(int *)value_ptr);
8500
8501     // check if any settings have been modified before saving them
8502     if (value != default_value)
8503       modified = TRUE;
8504
8505     // do not save if explicitly told or if unmodified default settings
8506     if ((save_type == SAVE_CONF_NEVER) ||
8507         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8508       return 0;
8509
8510     if (write_element)
8511       num_bytes += putFile16BitBE(file, element);
8512
8513     num_bytes += putFile8Bit(file, conf_type);
8514     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8515                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8516                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8517                   0);
8518   }
8519   else if (data_type == TYPE_STRING)
8520   {
8521     char *default_string = entry->default_string;
8522     char *string = (char *)(entry->value);
8523     int string_length = strlen(string);
8524     int i;
8525
8526     // check if any settings have been modified before saving them
8527     if (!strEqual(string, default_string))
8528       modified = TRUE;
8529
8530     // do not save if explicitly told or if unmodified default settings
8531     if ((save_type == SAVE_CONF_NEVER) ||
8532         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8533       return 0;
8534
8535     if (write_element)
8536       num_bytes += putFile16BitBE(file, element);
8537
8538     num_bytes += putFile8Bit(file, conf_type);
8539     num_bytes += putFile16BitBE(file, string_length);
8540
8541     for (i = 0; i < string_length; i++)
8542       num_bytes += putFile8Bit(file, string[i]);
8543   }
8544   else if (data_type == TYPE_ELEMENT_LIST)
8545   {
8546     int *element_array = (int *)(entry->value);
8547     int num_elements = *(int *)(entry->num_entities);
8548     int i;
8549
8550     // check if any settings have been modified before saving them
8551     for (i = 0; i < num_elements; i++)
8552       if (element_array[i] != 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_elements * CONF_ELEMENT_NUM_BYTES);
8565
8566     for (i = 0; i < num_elements; i++)
8567       num_bytes += putFile16BitBE(file, element_array[i]);
8568   }
8569   else if (data_type == TYPE_CONTENT_LIST)
8570   {
8571     struct Content *content = (struct Content *)(entry->value);
8572     int num_contents = *(int *)(entry->num_entities);
8573     int i, x, y;
8574
8575     // check if any settings have been modified before saving them
8576     for (i = 0; i < num_contents; i++)
8577       for (y = 0; y < 3; y++)
8578         for (x = 0; x < 3; x++)
8579           if (content[i].e[x][y] != default_value)
8580             modified = TRUE;
8581
8582     // do not save if explicitly told or if unmodified default settings
8583     if ((save_type == SAVE_CONF_NEVER) ||
8584         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8585       return 0;
8586
8587     if (write_element)
8588       num_bytes += putFile16BitBE(file, element);
8589
8590     num_bytes += putFile8Bit(file, conf_type);
8591     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8592
8593     for (i = 0; i < num_contents; i++)
8594       for (y = 0; y < 3; y++)
8595         for (x = 0; x < 3; x++)
8596           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8597   }
8598
8599   return num_bytes;
8600 }
8601
8602 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8603 {
8604   int chunk_size = 0;
8605   int i;
8606
8607   li = *level;          // copy level data into temporary buffer
8608
8609   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8610     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8611
8612   return chunk_size;
8613 }
8614
8615 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8616 {
8617   int chunk_size = 0;
8618   int i;
8619
8620   li = *level;          // copy level data into temporary buffer
8621
8622   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8623     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8624
8625   return chunk_size;
8626 }
8627
8628 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8629 {
8630   int envelope_nr = element - EL_ENVELOPE_1;
8631   int chunk_size = 0;
8632   int i;
8633
8634   chunk_size += putFile16BitBE(file, element);
8635
8636   // copy envelope data into temporary buffer
8637   xx_envelope = level->envelope[envelope_nr];
8638
8639   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8640     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8641
8642   return chunk_size;
8643 }
8644
8645 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8646 {
8647   struct ElementInfo *ei = &element_info[element];
8648   int chunk_size = 0;
8649   int i, j;
8650
8651   chunk_size += putFile16BitBE(file, element);
8652
8653   xx_ei = *ei;          // copy element data into temporary buffer
8654
8655   // set default description string for this specific element
8656   strcpy(xx_default_description, getDefaultElementDescription(ei));
8657
8658   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8659     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8660
8661   for (i = 0; i < ei->num_change_pages; i++)
8662   {
8663     struct ElementChangeInfo *change = &ei->change_page[i];
8664
8665     xx_current_change_page = i;
8666
8667     xx_change = *change;        // copy change data into temporary buffer
8668
8669     resetEventBits();
8670     setEventBitsFromEventFlags(change);
8671
8672     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8673       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8674                                          FALSE);
8675   }
8676
8677   return chunk_size;
8678 }
8679
8680 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8681 {
8682   struct ElementInfo *ei = &element_info[element];
8683   struct ElementGroupInfo *group = ei->group;
8684   int chunk_size = 0;
8685   int i;
8686
8687   chunk_size += putFile16BitBE(file, element);
8688
8689   xx_ei = *ei;          // copy element data into temporary buffer
8690   xx_group = *group;    // copy group data into temporary buffer
8691
8692   // set default description string for this specific element
8693   strcpy(xx_default_description, getDefaultElementDescription(ei));
8694
8695   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8696     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8697
8698   return chunk_size;
8699 }
8700
8701 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8702 {
8703   struct ElementInfo *ei = &element_info[element];
8704   int chunk_size = 0;
8705   int i;
8706
8707   chunk_size += putFile16BitBE(file, element);
8708
8709   xx_ei = *ei;          // copy element data into temporary buffer
8710
8711   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8712     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8713
8714   return chunk_size;
8715 }
8716
8717 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8718                                   boolean save_as_template)
8719 {
8720   int chunk_size;
8721   int i;
8722   FILE *file;
8723
8724   if (!(file = fopen(filename, MODE_WRITE)))
8725   {
8726     Warn("cannot save level file '%s'", filename);
8727
8728     return;
8729   }
8730
8731   level->file_version = FILE_VERSION_ACTUAL;
8732   level->game_version = GAME_VERSION_ACTUAL;
8733
8734   level->creation_date = getCurrentDate();
8735
8736   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8737   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8738
8739   chunk_size = SaveLevel_VERS(NULL, level);
8740   putFileChunkBE(file, "VERS", chunk_size);
8741   SaveLevel_VERS(file, level);
8742
8743   chunk_size = SaveLevel_DATE(NULL, level);
8744   putFileChunkBE(file, "DATE", chunk_size);
8745   SaveLevel_DATE(file, level);
8746
8747   chunk_size = SaveLevel_NAME(NULL, level);
8748   putFileChunkBE(file, "NAME", chunk_size);
8749   SaveLevel_NAME(file, level);
8750
8751   chunk_size = SaveLevel_AUTH(NULL, level);
8752   putFileChunkBE(file, "AUTH", chunk_size);
8753   SaveLevel_AUTH(file, level);
8754
8755   chunk_size = SaveLevel_INFO(NULL, level);
8756   putFileChunkBE(file, "INFO", chunk_size);
8757   SaveLevel_INFO(file, level);
8758
8759   chunk_size = SaveLevel_BODY(NULL, level);
8760   putFileChunkBE(file, "BODY", chunk_size);
8761   SaveLevel_BODY(file, level);
8762
8763   chunk_size = SaveLevel_ELEM(NULL, level);
8764   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8765   {
8766     putFileChunkBE(file, "ELEM", chunk_size);
8767     SaveLevel_ELEM(file, level);
8768   }
8769
8770   for (i = 0; i < NUM_ENVELOPES; i++)
8771   {
8772     int element = EL_ENVELOPE_1 + i;
8773
8774     chunk_size = SaveLevel_NOTE(NULL, level, element);
8775     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8776     {
8777       putFileChunkBE(file, "NOTE", chunk_size);
8778       SaveLevel_NOTE(file, level, element);
8779     }
8780   }
8781
8782   // if not using template level, check for non-default custom/group elements
8783   if (!level->use_custom_template || save_as_template)
8784   {
8785     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8786     {
8787       int element = EL_CUSTOM_START + i;
8788
8789       chunk_size = SaveLevel_CUSX(NULL, level, element);
8790       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8791       {
8792         putFileChunkBE(file, "CUSX", chunk_size);
8793         SaveLevel_CUSX(file, level, element);
8794       }
8795     }
8796
8797     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8798     {
8799       int element = EL_GROUP_START + i;
8800
8801       chunk_size = SaveLevel_GRPX(NULL, level, element);
8802       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8803       {
8804         putFileChunkBE(file, "GRPX", chunk_size);
8805         SaveLevel_GRPX(file, level, element);
8806       }
8807     }
8808
8809     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8810     {
8811       int element = GET_EMPTY_ELEMENT(i);
8812
8813       chunk_size = SaveLevel_EMPX(NULL, level, element);
8814       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8815       {
8816         putFileChunkBE(file, "EMPX", chunk_size);
8817         SaveLevel_EMPX(file, level, element);
8818       }
8819     }
8820   }
8821
8822   fclose(file);
8823
8824   SetFilePermissions(filename, PERMS_PRIVATE);
8825 }
8826
8827 void SaveLevel(int nr)
8828 {
8829   char *filename = getDefaultLevelFilename(nr);
8830
8831   SaveLevelFromFilename(&level, filename, FALSE);
8832 }
8833
8834 void SaveLevelTemplate(void)
8835 {
8836   char *filename = getLocalLevelTemplateFilename();
8837
8838   SaveLevelFromFilename(&level, filename, TRUE);
8839 }
8840
8841 boolean SaveLevelChecked(int nr)
8842 {
8843   char *filename = getDefaultLevelFilename(nr);
8844   boolean new_level = !fileExists(filename);
8845   boolean level_saved = FALSE;
8846
8847   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8848   {
8849     SaveLevel(nr);
8850
8851     if (new_level)
8852       Request("Level saved!", REQ_CONFIRM);
8853
8854     level_saved = TRUE;
8855   }
8856
8857   return level_saved;
8858 }
8859
8860 void DumpLevel(struct LevelInfo *level)
8861 {
8862   if (level->no_level_file || level->no_valid_file)
8863   {
8864     Warn("cannot dump -- no valid level file found");
8865
8866     return;
8867   }
8868
8869   PrintLine("-", 79);
8870   Print("Level xxx (file version %08d, game version %08d)\n",
8871         level->file_version, level->game_version);
8872   PrintLine("-", 79);
8873
8874   Print("Level author: '%s'\n", level->author);
8875   Print("Level title:  '%s'\n", level->name);
8876   Print("\n");
8877   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8878   Print("\n");
8879   Print("Level time:  %d seconds\n", level->time);
8880   Print("Gems needed: %d\n", level->gems_needed);
8881   Print("\n");
8882   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8883   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8884   Print("Time for light:      %d seconds\n", level->time_light);
8885   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8886   Print("\n");
8887   Print("Amoeba speed: %d\n", level->amoeba_speed);
8888   Print("\n");
8889
8890   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8891   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8892   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8893   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8894   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8895   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8896
8897   if (options.debug)
8898   {
8899     int i, j;
8900
8901     for (i = 0; i < NUM_ENVELOPES; i++)
8902     {
8903       char *text = level->envelope[i].text;
8904       int text_len = strlen(text);
8905       boolean has_text = FALSE;
8906
8907       for (j = 0; j < text_len; j++)
8908         if (text[j] != ' ' && text[j] != '\n')
8909           has_text = TRUE;
8910
8911       if (has_text)
8912       {
8913         Print("\n");
8914         Print("Envelope %d:\n'%s'\n", i + 1, text);
8915       }
8916     }
8917   }
8918
8919   PrintLine("-", 79);
8920 }
8921
8922 void DumpLevels(void)
8923 {
8924   static LevelDirTree *dumplevel_leveldir = NULL;
8925
8926   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8927                                                  global.dumplevel_leveldir);
8928
8929   if (dumplevel_leveldir == NULL)
8930     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8931
8932   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8933       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8934     Fail("no such level number: %d", global.dumplevel_level_nr);
8935
8936   leveldir_current = dumplevel_leveldir;
8937
8938   LoadLevel(global.dumplevel_level_nr);
8939   DumpLevel(&level);
8940
8941   CloseAllAndExit(0);
8942 }
8943
8944
8945 // ============================================================================
8946 // tape file functions
8947 // ============================================================================
8948
8949 static void setTapeInfoToDefaults(void)
8950 {
8951   int i;
8952
8953   // always start with reliable default values (empty tape)
8954   TapeErase();
8955
8956   // default values (also for pre-1.2 tapes) with only the first player
8957   tape.player_participates[0] = TRUE;
8958   for (i = 1; i < MAX_PLAYERS; i++)
8959     tape.player_participates[i] = FALSE;
8960
8961   // at least one (default: the first) player participates in every tape
8962   tape.num_participating_players = 1;
8963
8964   tape.property_bits = TAPE_PROPERTY_NONE;
8965
8966   tape.level_nr = level_nr;
8967   tape.counter = 0;
8968   tape.changed = FALSE;
8969   tape.solved = FALSE;
8970
8971   tape.recording = FALSE;
8972   tape.playing = FALSE;
8973   tape.pausing = FALSE;
8974
8975   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8976   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8977
8978   tape.no_info_chunk = TRUE;
8979   tape.no_valid_file = FALSE;
8980 }
8981
8982 static int getTapePosSize(struct TapeInfo *tape)
8983 {
8984   int tape_pos_size = 0;
8985
8986   if (tape->use_key_actions)
8987     tape_pos_size += tape->num_participating_players;
8988
8989   if (tape->use_mouse_actions)
8990     tape_pos_size += 3;         // x and y position and mouse button mask
8991
8992   tape_pos_size += 1;           // tape action delay value
8993
8994   return tape_pos_size;
8995 }
8996
8997 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8998 {
8999   tape->use_key_actions = FALSE;
9000   tape->use_mouse_actions = FALSE;
9001
9002   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9003     tape->use_key_actions = TRUE;
9004
9005   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9006     tape->use_mouse_actions = TRUE;
9007 }
9008
9009 static int getTapeActionValue(struct TapeInfo *tape)
9010 {
9011   return (tape->use_key_actions &&
9012           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9013           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9014           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9015           TAPE_ACTIONS_DEFAULT);
9016 }
9017
9018 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9019 {
9020   tape->file_version = getFileVersion(file);
9021   tape->game_version = getFileVersion(file);
9022
9023   return chunk_size;
9024 }
9025
9026 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9027 {
9028   int i;
9029
9030   tape->random_seed = getFile32BitBE(file);
9031   tape->date        = getFile32BitBE(file);
9032   tape->length      = getFile32BitBE(file);
9033
9034   // read header fields that are new since version 1.2
9035   if (tape->file_version >= FILE_VERSION_1_2)
9036   {
9037     byte store_participating_players = getFile8Bit(file);
9038     int engine_version;
9039
9040     // since version 1.2, tapes store which players participate in the tape
9041     tape->num_participating_players = 0;
9042     for (i = 0; i < MAX_PLAYERS; i++)
9043     {
9044       tape->player_participates[i] = FALSE;
9045
9046       if (store_participating_players & (1 << i))
9047       {
9048         tape->player_participates[i] = TRUE;
9049         tape->num_participating_players++;
9050       }
9051     }
9052
9053     setTapeActionFlags(tape, getFile8Bit(file));
9054
9055     tape->property_bits = getFile8Bit(file);
9056     tape->solved = getFile8Bit(file);
9057
9058     engine_version = getFileVersion(file);
9059     if (engine_version > 0)
9060       tape->engine_version = engine_version;
9061     else
9062       tape->engine_version = tape->game_version;
9063   }
9064
9065   return chunk_size;
9066 }
9067
9068 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9069 {
9070   tape->scr_fieldx = getFile8Bit(file);
9071   tape->scr_fieldy = getFile8Bit(file);
9072
9073   return chunk_size;
9074 }
9075
9076 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9077 {
9078   char *level_identifier = NULL;
9079   int level_identifier_size;
9080   int i;
9081
9082   tape->no_info_chunk = FALSE;
9083
9084   level_identifier_size = getFile16BitBE(file);
9085
9086   level_identifier = checked_malloc(level_identifier_size);
9087
9088   for (i = 0; i < level_identifier_size; i++)
9089     level_identifier[i] = getFile8Bit(file);
9090
9091   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9092   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9093
9094   checked_free(level_identifier);
9095
9096   tape->level_nr = getFile16BitBE(file);
9097
9098   chunk_size = 2 + level_identifier_size + 2;
9099
9100   return chunk_size;
9101 }
9102
9103 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9104 {
9105   int i, j;
9106   int tape_pos_size = getTapePosSize(tape);
9107   int chunk_size_expected = tape_pos_size * tape->length;
9108
9109   if (chunk_size_expected != chunk_size)
9110   {
9111     ReadUnusedBytesFromFile(file, chunk_size);
9112     return chunk_size_expected;
9113   }
9114
9115   for (i = 0; i < tape->length; i++)
9116   {
9117     if (i >= MAX_TAPE_LEN)
9118     {
9119       Warn("tape truncated -- size exceeds maximum tape size %d",
9120             MAX_TAPE_LEN);
9121
9122       // tape too large; read and ignore remaining tape data from this chunk
9123       for (;i < tape->length; i++)
9124         ReadUnusedBytesFromFile(file, tape_pos_size);
9125
9126       break;
9127     }
9128
9129     if (tape->use_key_actions)
9130     {
9131       for (j = 0; j < MAX_PLAYERS; j++)
9132       {
9133         tape->pos[i].action[j] = MV_NONE;
9134
9135         if (tape->player_participates[j])
9136           tape->pos[i].action[j] = getFile8Bit(file);
9137       }
9138     }
9139
9140     if (tape->use_mouse_actions)
9141     {
9142       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9143       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9144       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9145     }
9146
9147     tape->pos[i].delay = getFile8Bit(file);
9148
9149     if (tape->file_version == FILE_VERSION_1_0)
9150     {
9151       // eliminate possible diagonal moves in old tapes
9152       // this is only for backward compatibility
9153
9154       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9155       byte action = tape->pos[i].action[0];
9156       int k, num_moves = 0;
9157
9158       for (k = 0; k < 4; k++)
9159       {
9160         if (action & joy_dir[k])
9161         {
9162           tape->pos[i + num_moves].action[0] = joy_dir[k];
9163           if (num_moves > 0)
9164             tape->pos[i + num_moves].delay = 0;
9165           num_moves++;
9166         }
9167       }
9168
9169       if (num_moves > 1)
9170       {
9171         num_moves--;
9172         i += num_moves;
9173         tape->length += num_moves;
9174       }
9175     }
9176     else if (tape->file_version < FILE_VERSION_2_0)
9177     {
9178       // convert pre-2.0 tapes to new tape format
9179
9180       if (tape->pos[i].delay > 1)
9181       {
9182         // action part
9183         tape->pos[i + 1] = tape->pos[i];
9184         tape->pos[i + 1].delay = 1;
9185
9186         // delay part
9187         for (j = 0; j < MAX_PLAYERS; j++)
9188           tape->pos[i].action[j] = MV_NONE;
9189         tape->pos[i].delay--;
9190
9191         i++;
9192         tape->length++;
9193       }
9194     }
9195
9196     if (checkEndOfFile(file))
9197       break;
9198   }
9199
9200   if (i != tape->length)
9201     chunk_size = tape_pos_size * i;
9202
9203   return chunk_size;
9204 }
9205
9206 static void LoadTape_SokobanSolution(char *filename)
9207 {
9208   File *file;
9209   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9210
9211   if (!(file = openFile(filename, MODE_READ)))
9212   {
9213     tape.no_valid_file = TRUE;
9214
9215     return;
9216   }
9217
9218   while (!checkEndOfFile(file))
9219   {
9220     unsigned char c = getByteFromFile(file);
9221
9222     if (checkEndOfFile(file))
9223       break;
9224
9225     switch (c)
9226     {
9227       case 'u':
9228       case 'U':
9229         tape.pos[tape.length].action[0] = MV_UP;
9230         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9231         tape.length++;
9232         break;
9233
9234       case 'd':
9235       case 'D':
9236         tape.pos[tape.length].action[0] = MV_DOWN;
9237         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9238         tape.length++;
9239         break;
9240
9241       case 'l':
9242       case 'L':
9243         tape.pos[tape.length].action[0] = MV_LEFT;
9244         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9245         tape.length++;
9246         break;
9247
9248       case 'r':
9249       case 'R':
9250         tape.pos[tape.length].action[0] = MV_RIGHT;
9251         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9252         tape.length++;
9253         break;
9254
9255       case '\n':
9256       case '\r':
9257       case '\t':
9258       case ' ':
9259         // ignore white-space characters
9260         break;
9261
9262       default:
9263         tape.no_valid_file = TRUE;
9264
9265         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9266
9267         break;
9268     }
9269   }
9270
9271   closeFile(file);
9272
9273   if (tape.no_valid_file)
9274     return;
9275
9276   tape.length_frames  = GetTapeLengthFrames();
9277   tape.length_seconds = GetTapeLengthSeconds();
9278 }
9279
9280 void LoadTapeFromFilename(char *filename)
9281 {
9282   char cookie[MAX_LINE_LEN];
9283   char chunk_name[CHUNK_ID_LEN + 1];
9284   File *file;
9285   int chunk_size;
9286
9287   // always start with reliable default values
9288   setTapeInfoToDefaults();
9289
9290   if (strSuffix(filename, ".sln"))
9291   {
9292     LoadTape_SokobanSolution(filename);
9293
9294     return;
9295   }
9296
9297   if (!(file = openFile(filename, MODE_READ)))
9298   {
9299     tape.no_valid_file = TRUE;
9300
9301     return;
9302   }
9303
9304   getFileChunkBE(file, chunk_name, NULL);
9305   if (strEqual(chunk_name, "RND1"))
9306   {
9307     getFile32BitBE(file);               // not used
9308
9309     getFileChunkBE(file, chunk_name, NULL);
9310     if (!strEqual(chunk_name, "TAPE"))
9311     {
9312       tape.no_valid_file = TRUE;
9313
9314       Warn("unknown format of tape file '%s'", filename);
9315
9316       closeFile(file);
9317
9318       return;
9319     }
9320   }
9321   else  // check for pre-2.0 file format with cookie string
9322   {
9323     strcpy(cookie, chunk_name);
9324     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9325       cookie[4] = '\0';
9326     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9327       cookie[strlen(cookie) - 1] = '\0';
9328
9329     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9330     {
9331       tape.no_valid_file = TRUE;
9332
9333       Warn("unknown format of tape file '%s'", filename);
9334
9335       closeFile(file);
9336
9337       return;
9338     }
9339
9340     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9341     {
9342       tape.no_valid_file = TRUE;
9343
9344       Warn("unsupported version of tape file '%s'", filename);
9345
9346       closeFile(file);
9347
9348       return;
9349     }
9350
9351     // pre-2.0 tape files have no game version, so use file version here
9352     tape.game_version = tape.file_version;
9353   }
9354
9355   if (tape.file_version < FILE_VERSION_1_2)
9356   {
9357     // tape files from versions before 1.2.0 without chunk structure
9358     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9359     LoadTape_BODY(file, 2 * tape.length,      &tape);
9360   }
9361   else
9362   {
9363     static struct
9364     {
9365       char *name;
9366       int size;
9367       int (*loader)(File *, int, struct TapeInfo *);
9368     }
9369     chunk_info[] =
9370     {
9371       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9372       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9373       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9374       { "INFO", -1,                     LoadTape_INFO },
9375       { "BODY", -1,                     LoadTape_BODY },
9376       {  NULL,  0,                      NULL }
9377     };
9378
9379     while (getFileChunkBE(file, chunk_name, &chunk_size))
9380     {
9381       int i = 0;
9382
9383       while (chunk_info[i].name != NULL &&
9384              !strEqual(chunk_name, chunk_info[i].name))
9385         i++;
9386
9387       if (chunk_info[i].name == NULL)
9388       {
9389         Warn("unknown chunk '%s' in tape file '%s'",
9390               chunk_name, filename);
9391
9392         ReadUnusedBytesFromFile(file, chunk_size);
9393       }
9394       else if (chunk_info[i].size != -1 &&
9395                chunk_info[i].size != chunk_size)
9396       {
9397         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9398               chunk_size, chunk_name, filename);
9399
9400         ReadUnusedBytesFromFile(file, chunk_size);
9401       }
9402       else
9403       {
9404         // call function to load this tape chunk
9405         int chunk_size_expected =
9406           (chunk_info[i].loader)(file, chunk_size, &tape);
9407
9408         // the size of some chunks cannot be checked before reading other
9409         // chunks first (like "HEAD" and "BODY") that contain some header
9410         // information, so check them here
9411         if (chunk_size_expected != chunk_size)
9412         {
9413           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9414                 chunk_size, chunk_name, filename);
9415         }
9416       }
9417     }
9418   }
9419
9420   closeFile(file);
9421
9422   tape.length_frames  = GetTapeLengthFrames();
9423   tape.length_seconds = GetTapeLengthSeconds();
9424
9425 #if 0
9426   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9427         tape.file_version);
9428   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9429         tape.game_version);
9430   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9431         tape.engine_version);
9432 #endif
9433 }
9434
9435 void LoadTape(int nr)
9436 {
9437   char *filename = getTapeFilename(nr);
9438
9439   LoadTapeFromFilename(filename);
9440 }
9441
9442 void LoadSolutionTape(int nr)
9443 {
9444   char *filename = getSolutionTapeFilename(nr);
9445
9446   LoadTapeFromFilename(filename);
9447
9448   if (TAPE_IS_EMPTY(tape))
9449   {
9450     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9451         level.native_bd_level->replay != NULL)
9452       CopyNativeTape_BD_to_RND(&level);
9453     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9454         level.native_sp_level->demo.is_available)
9455       CopyNativeTape_SP_to_RND(&level);
9456   }
9457 }
9458
9459 void LoadScoreTape(char *score_tape_basename, int nr)
9460 {
9461   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9462
9463   LoadTapeFromFilename(filename);
9464 }
9465
9466 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9467 {
9468   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9469
9470   LoadTapeFromFilename(filename);
9471 }
9472
9473 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9474 {
9475   // chunk required for team mode tapes with non-default screen size
9476   return (tape->num_participating_players > 1 &&
9477           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9478            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9479 }
9480
9481 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9482 {
9483   putFileVersion(file, tape->file_version);
9484   putFileVersion(file, tape->game_version);
9485 }
9486
9487 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9488 {
9489   int i;
9490   byte store_participating_players = 0;
9491
9492   // set bits for participating players for compact storage
9493   for (i = 0; i < MAX_PLAYERS; i++)
9494     if (tape->player_participates[i])
9495       store_participating_players |= (1 << i);
9496
9497   putFile32BitBE(file, tape->random_seed);
9498   putFile32BitBE(file, tape->date);
9499   putFile32BitBE(file, tape->length);
9500
9501   putFile8Bit(file, store_participating_players);
9502
9503   putFile8Bit(file, getTapeActionValue(tape));
9504
9505   putFile8Bit(file, tape->property_bits);
9506   putFile8Bit(file, tape->solved);
9507
9508   putFileVersion(file, tape->engine_version);
9509 }
9510
9511 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9512 {
9513   putFile8Bit(file, tape->scr_fieldx);
9514   putFile8Bit(file, tape->scr_fieldy);
9515 }
9516
9517 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9518 {
9519   int level_identifier_size = strlen(tape->level_identifier) + 1;
9520   int i;
9521
9522   putFile16BitBE(file, level_identifier_size);
9523
9524   for (i = 0; i < level_identifier_size; i++)
9525     putFile8Bit(file, tape->level_identifier[i]);
9526
9527   putFile16BitBE(file, tape->level_nr);
9528 }
9529
9530 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9531 {
9532   int i, j;
9533
9534   for (i = 0; i < tape->length; i++)
9535   {
9536     if (tape->use_key_actions)
9537     {
9538       for (j = 0; j < MAX_PLAYERS; j++)
9539         if (tape->player_participates[j])
9540           putFile8Bit(file, tape->pos[i].action[j]);
9541     }
9542
9543     if (tape->use_mouse_actions)
9544     {
9545       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9546       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9547       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9548     }
9549
9550     putFile8Bit(file, tape->pos[i].delay);
9551   }
9552 }
9553
9554 void SaveTapeToFilename(char *filename)
9555 {
9556   FILE *file;
9557   int tape_pos_size;
9558   int info_chunk_size;
9559   int body_chunk_size;
9560
9561   if (!(file = fopen(filename, MODE_WRITE)))
9562   {
9563     Warn("cannot save level recording file '%s'", filename);
9564
9565     return;
9566   }
9567
9568   tape_pos_size = getTapePosSize(&tape);
9569
9570   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9571   body_chunk_size = tape_pos_size * tape.length;
9572
9573   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9574   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9575
9576   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9577   SaveTape_VERS(file, &tape);
9578
9579   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9580   SaveTape_HEAD(file, &tape);
9581
9582   if (checkSaveTape_SCRN(&tape))
9583   {
9584     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9585     SaveTape_SCRN(file, &tape);
9586   }
9587
9588   putFileChunkBE(file, "INFO", info_chunk_size);
9589   SaveTape_INFO(file, &tape);
9590
9591   putFileChunkBE(file, "BODY", body_chunk_size);
9592   SaveTape_BODY(file, &tape);
9593
9594   fclose(file);
9595
9596   SetFilePermissions(filename, PERMS_PRIVATE);
9597 }
9598
9599 static void SaveTapeExt(char *filename)
9600 {
9601   int i;
9602
9603   tape.file_version = FILE_VERSION_ACTUAL;
9604   tape.game_version = GAME_VERSION_ACTUAL;
9605
9606   tape.num_participating_players = 0;
9607
9608   // count number of participating players
9609   for (i = 0; i < MAX_PLAYERS; i++)
9610     if (tape.player_participates[i])
9611       tape.num_participating_players++;
9612
9613   SaveTapeToFilename(filename);
9614
9615   tape.changed = FALSE;
9616 }
9617
9618 void SaveTape(int nr)
9619 {
9620   char *filename = getTapeFilename(nr);
9621
9622   InitTapeDirectory(leveldir_current->subdir);
9623
9624   SaveTapeExt(filename);
9625 }
9626
9627 void SaveScoreTape(int nr)
9628 {
9629   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9630
9631   // used instead of "leveldir_current->subdir" (for network games)
9632   InitScoreTapeDirectory(levelset.identifier, nr);
9633
9634   SaveTapeExt(filename);
9635 }
9636
9637 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9638                                   unsigned int req_state_added)
9639 {
9640   char *filename = getTapeFilename(nr);
9641   boolean new_tape = !fileExists(filename);
9642   boolean tape_saved = FALSE;
9643
9644   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9645   {
9646     SaveTape(nr);
9647
9648     if (new_tape)
9649       Request(msg_saved, REQ_CONFIRM | req_state_added);
9650
9651     tape_saved = TRUE;
9652   }
9653
9654   return tape_saved;
9655 }
9656
9657 boolean SaveTapeChecked(int nr)
9658 {
9659   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9660 }
9661
9662 boolean SaveTapeChecked_LevelSolved(int nr)
9663 {
9664   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9665                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9666 }
9667
9668 void DumpTape(struct TapeInfo *tape)
9669 {
9670   int tape_frame_counter;
9671   int i, j;
9672
9673   if (tape->no_valid_file)
9674   {
9675     Warn("cannot dump -- no valid tape file found");
9676
9677     return;
9678   }
9679
9680   PrintLine("-", 79);
9681
9682   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9683         tape->level_nr, tape->file_version, tape->game_version);
9684   Print("                  (effective engine version %08d)\n",
9685         tape->engine_version);
9686   Print("Level series identifier: '%s'\n", tape->level_identifier);
9687
9688   Print("Solution tape: %s\n",
9689         tape->solved ? "yes" :
9690         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9691
9692   Print("Special tape properties: ");
9693   if (tape->property_bits == TAPE_PROPERTY_NONE)
9694     Print("[none]");
9695   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9696     Print("[em_random_bug]");
9697   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9698     Print("[game_speed]");
9699   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9700     Print("[pause]");
9701   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9702     Print("[single_step]");
9703   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9704     Print("[snapshot]");
9705   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9706     Print("[replayed]");
9707   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9708     Print("[tas_keys]");
9709   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9710     Print("[small_graphics]");
9711   Print("\n");
9712
9713   int year2 = tape->date / 10000;
9714   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9715   int month_index_raw = (tape->date / 100) % 100;
9716   int month_index = month_index_raw % 12;       // prevent invalid index
9717   int month = month_index + 1;
9718   int day = tape->date % 100;
9719
9720   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9721
9722   PrintLine("-", 79);
9723
9724   tape_frame_counter = 0;
9725
9726   for (i = 0; i < tape->length; i++)
9727   {
9728     if (i >= MAX_TAPE_LEN)
9729       break;
9730
9731     Print("%04d: ", i);
9732
9733     for (j = 0; j < MAX_PLAYERS; j++)
9734     {
9735       if (tape->player_participates[j])
9736       {
9737         int action = tape->pos[i].action[j];
9738
9739         Print("%d:%02x ", j, action);
9740         Print("[%c%c%c%c|%c%c] - ",
9741               (action & JOY_LEFT ? '<' : ' '),
9742               (action & JOY_RIGHT ? '>' : ' '),
9743               (action & JOY_UP ? '^' : ' '),
9744               (action & JOY_DOWN ? 'v' : ' '),
9745               (action & JOY_BUTTON_1 ? '1' : ' '),
9746               (action & JOY_BUTTON_2 ? '2' : ' '));
9747       }
9748     }
9749
9750     Print("(%03d) ", tape->pos[i].delay);
9751     Print("[%05d]\n", tape_frame_counter);
9752
9753     tape_frame_counter += tape->pos[i].delay;
9754   }
9755
9756   PrintLine("-", 79);
9757 }
9758
9759 void DumpTapes(void)
9760 {
9761   static LevelDirTree *dumptape_leveldir = NULL;
9762
9763   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9764                                                 global.dumptape_leveldir);
9765
9766   if (dumptape_leveldir == NULL)
9767     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9768
9769   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9770       global.dumptape_level_nr > dumptape_leveldir->last_level)
9771     Fail("no such level number: %d", global.dumptape_level_nr);
9772
9773   leveldir_current = dumptape_leveldir;
9774
9775   if (options.mytapes)
9776     LoadTape(global.dumptape_level_nr);
9777   else
9778     LoadSolutionTape(global.dumptape_level_nr);
9779
9780   DumpTape(&tape);
9781
9782   CloseAllAndExit(0);
9783 }
9784
9785
9786 // ============================================================================
9787 // score file functions
9788 // ============================================================================
9789
9790 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9791 {
9792   int i;
9793
9794   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9795   {
9796     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9797     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9798     scores->entry[i].score = 0;
9799     scores->entry[i].time = 0;
9800
9801     scores->entry[i].id = -1;
9802     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9803     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9804     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9805     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9806     strcpy(scores->entry[i].country_code, "??");
9807   }
9808
9809   scores->num_entries = 0;
9810   scores->last_added = -1;
9811   scores->last_added_local = -1;
9812
9813   scores->updated = FALSE;
9814   scores->uploaded = FALSE;
9815   scores->tape_downloaded = FALSE;
9816   scores->force_last_added = FALSE;
9817
9818   // The following values are intentionally not reset here:
9819   // - last_level_nr
9820   // - last_entry_nr
9821   // - next_level_nr
9822   // - continue_playing
9823   // - continue_on_return
9824 }
9825
9826 static void setScoreInfoToDefaults(void)
9827 {
9828   setScoreInfoToDefaultsExt(&scores);
9829 }
9830
9831 static void setServerScoreInfoToDefaults(void)
9832 {
9833   setScoreInfoToDefaultsExt(&server_scores);
9834 }
9835
9836 static void LoadScore_OLD(int nr)
9837 {
9838   int i;
9839   char *filename = getScoreFilename(nr);
9840   char cookie[MAX_LINE_LEN];
9841   char line[MAX_LINE_LEN];
9842   char *line_ptr;
9843   FILE *file;
9844
9845   if (!(file = fopen(filename, MODE_READ)))
9846     return;
9847
9848   // check file identifier
9849   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9850     cookie[0] = '\0';
9851   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9852     cookie[strlen(cookie) - 1] = '\0';
9853
9854   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9855   {
9856     Warn("unknown format of score file '%s'", filename);
9857
9858     fclose(file);
9859
9860     return;
9861   }
9862
9863   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9864   {
9865     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9866       Warn("fscanf() failed; %s", strerror(errno));
9867
9868     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9869       line[0] = '\0';
9870
9871     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9872       line[strlen(line) - 1] = '\0';
9873
9874     for (line_ptr = line; *line_ptr; line_ptr++)
9875     {
9876       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9877       {
9878         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9879         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9880         break;
9881       }
9882     }
9883   }
9884
9885   fclose(file);
9886 }
9887
9888 static void ConvertScore_OLD(void)
9889 {
9890   // only convert score to time for levels that rate playing time over score
9891   if (!level.rate_time_over_score)
9892     return;
9893
9894   // convert old score to playing time for score-less levels (like Supaplex)
9895   int time_final_max = 999;
9896   int i;
9897
9898   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9899   {
9900     int score = scores.entry[i].score;
9901
9902     if (score > 0 && score < time_final_max)
9903       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9904   }
9905 }
9906
9907 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9908 {
9909   scores->file_version = getFileVersion(file);
9910   scores->game_version = getFileVersion(file);
9911
9912   return chunk_size;
9913 }
9914
9915 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9916 {
9917   char *level_identifier = NULL;
9918   int level_identifier_size;
9919   int i;
9920
9921   level_identifier_size = getFile16BitBE(file);
9922
9923   level_identifier = checked_malloc(level_identifier_size);
9924
9925   for (i = 0; i < level_identifier_size; i++)
9926     level_identifier[i] = getFile8Bit(file);
9927
9928   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9929   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9930
9931   checked_free(level_identifier);
9932
9933   scores->level_nr = getFile16BitBE(file);
9934   scores->num_entries = getFile16BitBE(file);
9935
9936   chunk_size = 2 + level_identifier_size + 2 + 2;
9937
9938   return chunk_size;
9939 }
9940
9941 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9942 {
9943   int i, j;
9944
9945   for (i = 0; i < scores->num_entries; i++)
9946   {
9947     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9948       scores->entry[i].name[j] = getFile8Bit(file);
9949
9950     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9951   }
9952
9953   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9954
9955   return chunk_size;
9956 }
9957
9958 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9959 {
9960   int i;
9961
9962   for (i = 0; i < scores->num_entries; i++)
9963     scores->entry[i].score = getFile16BitBE(file);
9964
9965   chunk_size = scores->num_entries * 2;
9966
9967   return chunk_size;
9968 }
9969
9970 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9971 {
9972   int i;
9973
9974   for (i = 0; i < scores->num_entries; i++)
9975     scores->entry[i].score = getFile32BitBE(file);
9976
9977   chunk_size = scores->num_entries * 4;
9978
9979   return chunk_size;
9980 }
9981
9982 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9983 {
9984   int i;
9985
9986   for (i = 0; i < scores->num_entries; i++)
9987     scores->entry[i].time = getFile32BitBE(file);
9988
9989   chunk_size = scores->num_entries * 4;
9990
9991   return chunk_size;
9992 }
9993
9994 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9995 {
9996   int i, j;
9997
9998   for (i = 0; i < scores->num_entries; i++)
9999   {
10000     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10001       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10002
10003     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10004   }
10005
10006   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10007
10008   return chunk_size;
10009 }
10010
10011 void LoadScore(int nr)
10012 {
10013   char *filename = getScoreFilename(nr);
10014   char cookie[MAX_LINE_LEN];
10015   char chunk_name[CHUNK_ID_LEN + 1];
10016   int chunk_size;
10017   boolean old_score_file_format = FALSE;
10018   File *file;
10019
10020   // always start with reliable default values
10021   setScoreInfoToDefaults();
10022
10023   if (!(file = openFile(filename, MODE_READ)))
10024     return;
10025
10026   getFileChunkBE(file, chunk_name, NULL);
10027   if (strEqual(chunk_name, "RND1"))
10028   {
10029     getFile32BitBE(file);               // not used
10030
10031     getFileChunkBE(file, chunk_name, NULL);
10032     if (!strEqual(chunk_name, "SCOR"))
10033     {
10034       Warn("unknown format of score file '%s'", filename);
10035
10036       closeFile(file);
10037
10038       return;
10039     }
10040   }
10041   else  // check for old file format with cookie string
10042   {
10043     strcpy(cookie, chunk_name);
10044     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10045       cookie[4] = '\0';
10046     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10047       cookie[strlen(cookie) - 1] = '\0';
10048
10049     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10050     {
10051       Warn("unknown format of score file '%s'", filename);
10052
10053       closeFile(file);
10054
10055       return;
10056     }
10057
10058     old_score_file_format = TRUE;
10059   }
10060
10061   if (old_score_file_format)
10062   {
10063     // score files from versions before 4.2.4.0 without chunk structure
10064     LoadScore_OLD(nr);
10065
10066     // convert score to time, if possible (mainly for Supaplex levels)
10067     ConvertScore_OLD();
10068   }
10069   else
10070   {
10071     static struct
10072     {
10073       char *name;
10074       int size;
10075       int (*loader)(File *, int, struct ScoreInfo *);
10076     }
10077     chunk_info[] =
10078     {
10079       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10080       { "INFO", -1,                     LoadScore_INFO },
10081       { "NAME", -1,                     LoadScore_NAME },
10082       { "SCOR", -1,                     LoadScore_SCOR },
10083       { "SC4R", -1,                     LoadScore_SC4R },
10084       { "TIME", -1,                     LoadScore_TIME },
10085       { "TAPE", -1,                     LoadScore_TAPE },
10086
10087       {  NULL,  0,                      NULL }
10088     };
10089
10090     while (getFileChunkBE(file, chunk_name, &chunk_size))
10091     {
10092       int i = 0;
10093
10094       while (chunk_info[i].name != NULL &&
10095              !strEqual(chunk_name, chunk_info[i].name))
10096         i++;
10097
10098       if (chunk_info[i].name == NULL)
10099       {
10100         Warn("unknown chunk '%s' in score file '%s'",
10101               chunk_name, filename);
10102
10103         ReadUnusedBytesFromFile(file, chunk_size);
10104       }
10105       else if (chunk_info[i].size != -1 &&
10106                chunk_info[i].size != chunk_size)
10107       {
10108         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10109               chunk_size, chunk_name, filename);
10110
10111         ReadUnusedBytesFromFile(file, chunk_size);
10112       }
10113       else
10114       {
10115         // call function to load this score chunk
10116         int chunk_size_expected =
10117           (chunk_info[i].loader)(file, chunk_size, &scores);
10118
10119         // the size of some chunks cannot be checked before reading other
10120         // chunks first (like "HEAD" and "BODY") that contain some header
10121         // information, so check them here
10122         if (chunk_size_expected != chunk_size)
10123         {
10124           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10125                 chunk_size, chunk_name, filename);
10126         }
10127       }
10128     }
10129   }
10130
10131   closeFile(file);
10132 }
10133
10134 #if ENABLE_HISTORIC_CHUNKS
10135 void SaveScore_OLD(int nr)
10136 {
10137   int i;
10138   char *filename = getScoreFilename(nr);
10139   FILE *file;
10140
10141   // used instead of "leveldir_current->subdir" (for network games)
10142   InitScoreDirectory(levelset.identifier);
10143
10144   if (!(file = fopen(filename, MODE_WRITE)))
10145   {
10146     Warn("cannot save score for level %d", nr);
10147
10148     return;
10149   }
10150
10151   fprintf(file, "%s\n\n", SCORE_COOKIE);
10152
10153   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10154     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10155
10156   fclose(file);
10157
10158   SetFilePermissions(filename, PERMS_PRIVATE);
10159 }
10160 #endif
10161
10162 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10163 {
10164   putFileVersion(file, scores->file_version);
10165   putFileVersion(file, scores->game_version);
10166 }
10167
10168 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10169 {
10170   int level_identifier_size = strlen(scores->level_identifier) + 1;
10171   int i;
10172
10173   putFile16BitBE(file, level_identifier_size);
10174
10175   for (i = 0; i < level_identifier_size; i++)
10176     putFile8Bit(file, scores->level_identifier[i]);
10177
10178   putFile16BitBE(file, scores->level_nr);
10179   putFile16BitBE(file, scores->num_entries);
10180 }
10181
10182 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10183 {
10184   int i, j;
10185
10186   for (i = 0; i < scores->num_entries; i++)
10187   {
10188     int name_size = strlen(scores->entry[i].name);
10189
10190     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10191       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10192   }
10193 }
10194
10195 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10196 {
10197   int i;
10198
10199   for (i = 0; i < scores->num_entries; i++)
10200     putFile16BitBE(file, scores->entry[i].score);
10201 }
10202
10203 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10204 {
10205   int i;
10206
10207   for (i = 0; i < scores->num_entries; i++)
10208     putFile32BitBE(file, scores->entry[i].score);
10209 }
10210
10211 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10212 {
10213   int i;
10214
10215   for (i = 0; i < scores->num_entries; i++)
10216     putFile32BitBE(file, scores->entry[i].time);
10217 }
10218
10219 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10220 {
10221   int i, j;
10222
10223   for (i = 0; i < scores->num_entries; i++)
10224   {
10225     int size = strlen(scores->entry[i].tape_basename);
10226
10227     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10228       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10229   }
10230 }
10231
10232 static void SaveScoreToFilename(char *filename)
10233 {
10234   FILE *file;
10235   int info_chunk_size;
10236   int name_chunk_size;
10237   int scor_chunk_size;
10238   int sc4r_chunk_size;
10239   int time_chunk_size;
10240   int tape_chunk_size;
10241   boolean has_large_score_values;
10242   int i;
10243
10244   if (!(file = fopen(filename, MODE_WRITE)))
10245   {
10246     Warn("cannot save score file '%s'", filename);
10247
10248     return;
10249   }
10250
10251   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10252   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10253   scor_chunk_size = scores.num_entries * 2;
10254   sc4r_chunk_size = scores.num_entries * 4;
10255   time_chunk_size = scores.num_entries * 4;
10256   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10257
10258   has_large_score_values = FALSE;
10259   for (i = 0; i < scores.num_entries; i++)
10260     if (scores.entry[i].score > 0xffff)
10261       has_large_score_values = TRUE;
10262
10263   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10264   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10265
10266   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10267   SaveScore_VERS(file, &scores);
10268
10269   putFileChunkBE(file, "INFO", info_chunk_size);
10270   SaveScore_INFO(file, &scores);
10271
10272   putFileChunkBE(file, "NAME", name_chunk_size);
10273   SaveScore_NAME(file, &scores);
10274
10275   if (has_large_score_values)
10276   {
10277     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10278     SaveScore_SC4R(file, &scores);
10279   }
10280   else
10281   {
10282     putFileChunkBE(file, "SCOR", scor_chunk_size);
10283     SaveScore_SCOR(file, &scores);
10284   }
10285
10286   putFileChunkBE(file, "TIME", time_chunk_size);
10287   SaveScore_TIME(file, &scores);
10288
10289   putFileChunkBE(file, "TAPE", tape_chunk_size);
10290   SaveScore_TAPE(file, &scores);
10291
10292   fclose(file);
10293
10294   SetFilePermissions(filename, PERMS_PRIVATE);
10295 }
10296
10297 void SaveScore(int nr)
10298 {
10299   char *filename = getScoreFilename(nr);
10300   int i;
10301
10302   // used instead of "leveldir_current->subdir" (for network games)
10303   InitScoreDirectory(levelset.identifier);
10304
10305   scores.file_version = FILE_VERSION_ACTUAL;
10306   scores.game_version = GAME_VERSION_ACTUAL;
10307
10308   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10309   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10310   scores.level_nr = level_nr;
10311
10312   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10313     if (scores.entry[i].score == 0 &&
10314         scores.entry[i].time == 0 &&
10315         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10316       break;
10317
10318   scores.num_entries = i;
10319
10320   if (scores.num_entries == 0)
10321     return;
10322
10323   SaveScoreToFilename(filename);
10324 }
10325
10326 static void LoadServerScoreFromCache(int nr)
10327 {
10328   struct ScoreEntry score_entry;
10329   struct
10330   {
10331     void *value;
10332     boolean is_string;
10333     int string_size;
10334   }
10335   score_mapping[] =
10336   {
10337     { &score_entry.score,               FALSE,  0                       },
10338     { &score_entry.time,                FALSE,  0                       },
10339     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10340     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10341     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10342     { &score_entry.id,                  FALSE,  0                       },
10343     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10344     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10345     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10346     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10347
10348     { NULL,                             FALSE,  0                       }
10349   };
10350   char *filename = getScoreCacheFilename(nr);
10351   SetupFileHash *score_hash = loadSetupFileHash(filename);
10352   int i, j;
10353
10354   server_scores.num_entries = 0;
10355
10356   if (score_hash == NULL)
10357     return;
10358
10359   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10360   {
10361     score_entry = server_scores.entry[i];
10362
10363     for (j = 0; score_mapping[j].value != NULL; j++)
10364     {
10365       char token[10];
10366
10367       sprintf(token, "%02d.%d", i, j);
10368
10369       char *value = getHashEntry(score_hash, token);
10370
10371       if (value == NULL)
10372         continue;
10373
10374       if (score_mapping[j].is_string)
10375       {
10376         char *score_value = (char *)score_mapping[j].value;
10377         int value_size = score_mapping[j].string_size;
10378
10379         strncpy(score_value, value, value_size);
10380         score_value[value_size] = '\0';
10381       }
10382       else
10383       {
10384         int *score_value = (int *)score_mapping[j].value;
10385
10386         *score_value = atoi(value);
10387       }
10388
10389       server_scores.num_entries = i + 1;
10390     }
10391
10392     server_scores.entry[i] = score_entry;
10393   }
10394
10395   freeSetupFileHash(score_hash);
10396 }
10397
10398 void LoadServerScore(int nr, boolean download_score)
10399 {
10400   if (!setup.use_api_server)
10401     return;
10402
10403   // always start with reliable default values
10404   setServerScoreInfoToDefaults();
10405
10406   // 1st step: load server scores from cache file (which may not exist)
10407   // (this should prevent reading it while the thread is writing to it)
10408   LoadServerScoreFromCache(nr);
10409
10410   if (download_score && runtime.use_api_server)
10411   {
10412     // 2nd step: download server scores from score server to cache file
10413     // (as thread, as it might time out if the server is not reachable)
10414     ApiGetScoreAsThread(nr);
10415   }
10416 }
10417
10418 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10419 {
10420   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10421
10422   // if score tape not uploaded, ask for uploading missing tapes later
10423   if (!setup.has_remaining_tapes)
10424     setup.ask_for_remaining_tapes = TRUE;
10425
10426   setup.provide_uploading_tapes = TRUE;
10427   setup.has_remaining_tapes = TRUE;
10428
10429   SaveSetup_ServerSetup();
10430 }
10431
10432 void SaveServerScore(int nr, boolean tape_saved)
10433 {
10434   if (!runtime.use_api_server)
10435   {
10436     PrepareScoreTapesForUpload(leveldir_current->subdir);
10437
10438     return;
10439   }
10440
10441   ApiAddScoreAsThread(nr, tape_saved, NULL);
10442 }
10443
10444 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10445                              char *score_tape_filename)
10446 {
10447   if (!runtime.use_api_server)
10448     return;
10449
10450   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10451 }
10452
10453 void LoadLocalAndServerScore(int nr, boolean download_score)
10454 {
10455   int last_added_local = scores.last_added_local;
10456   boolean force_last_added = scores.force_last_added;
10457
10458   // needed if only showing server scores
10459   setScoreInfoToDefaults();
10460
10461   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10462     LoadScore(nr);
10463
10464   // restore last added local score entry (before merging server scores)
10465   scores.last_added = scores.last_added_local = last_added_local;
10466
10467   if (setup.use_api_server &&
10468       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10469   {
10470     // load server scores from cache file and trigger update from server
10471     LoadServerScore(nr, download_score);
10472
10473     // merge local scores with scores from server
10474     MergeServerScore();
10475   }
10476
10477   if (force_last_added)
10478     scores.force_last_added = force_last_added;
10479 }
10480
10481
10482 // ============================================================================
10483 // setup file functions
10484 // ============================================================================
10485
10486 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10487
10488
10489 static struct TokenInfo global_setup_tokens[] =
10490 {
10491   {
10492     TYPE_STRING,
10493     &setup.player_name,                         "player_name"
10494   },
10495   {
10496     TYPE_SWITCH,
10497     &setup.multiple_users,                      "multiple_users"
10498   },
10499   {
10500     TYPE_SWITCH,
10501     &setup.sound,                               "sound"
10502   },
10503   {
10504     TYPE_SWITCH,
10505     &setup.sound_loops,                         "repeating_sound_loops"
10506   },
10507   {
10508     TYPE_SWITCH,
10509     &setup.sound_music,                         "background_music"
10510   },
10511   {
10512     TYPE_SWITCH,
10513     &setup.sound_simple,                        "simple_sound_effects"
10514   },
10515   {
10516     TYPE_SWITCH,
10517     &setup.toons,                               "toons"
10518   },
10519   {
10520     TYPE_SWITCH,
10521     &setup.global_animations,                   "global_animations"
10522   },
10523   {
10524     TYPE_SWITCH,
10525     &setup.scroll_delay,                        "scroll_delay"
10526   },
10527   {
10528     TYPE_SWITCH,
10529     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10530   },
10531   {
10532     TYPE_INTEGER,
10533     &setup.scroll_delay_value,                  "scroll_delay_value"
10534   },
10535   {
10536     TYPE_STRING,
10537     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10538   },
10539   {
10540     TYPE_INTEGER,
10541     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10542   },
10543   {
10544     TYPE_SWITCH,
10545     &setup.fade_screens,                        "fade_screens"
10546   },
10547   {
10548     TYPE_SWITCH,
10549     &setup.autorecord,                          "automatic_tape_recording"
10550   },
10551   {
10552     TYPE_SWITCH,
10553     &setup.autorecord_after_replay,             "autorecord_after_replay"
10554   },
10555   {
10556     TYPE_SWITCH,
10557     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10558   },
10559   {
10560     TYPE_SWITCH,
10561     &setup.show_titlescreen,                    "show_titlescreen"
10562   },
10563   {
10564     TYPE_SWITCH,
10565     &setup.quick_doors,                         "quick_doors"
10566   },
10567   {
10568     TYPE_SWITCH,
10569     &setup.team_mode,                           "team_mode"
10570   },
10571   {
10572     TYPE_SWITCH,
10573     &setup.handicap,                            "handicap"
10574   },
10575   {
10576     TYPE_SWITCH,
10577     &setup.skip_levels,                         "skip_levels"
10578   },
10579   {
10580     TYPE_SWITCH,
10581     &setup.increment_levels,                    "increment_levels"
10582   },
10583   {
10584     TYPE_SWITCH,
10585     &setup.auto_play_next_level,                "auto_play_next_level"
10586   },
10587   {
10588     TYPE_SWITCH,
10589     &setup.count_score_after_game,              "count_score_after_game"
10590   },
10591   {
10592     TYPE_SWITCH,
10593     &setup.show_scores_after_game,              "show_scores_after_game"
10594   },
10595   {
10596     TYPE_SWITCH,
10597     &setup.time_limit,                          "time_limit"
10598   },
10599   {
10600     TYPE_SWITCH,
10601     &setup.fullscreen,                          "fullscreen"
10602   },
10603   {
10604     TYPE_INTEGER,
10605     &setup.window_scaling_percent,              "window_scaling_percent"
10606   },
10607   {
10608     TYPE_STRING,
10609     &setup.window_scaling_quality,              "window_scaling_quality"
10610   },
10611   {
10612     TYPE_STRING,
10613     &setup.screen_rendering_mode,               "screen_rendering_mode"
10614   },
10615   {
10616     TYPE_STRING,
10617     &setup.vsync_mode,                          "vsync_mode"
10618   },
10619   {
10620     TYPE_SWITCH,
10621     &setup.ask_on_escape,                       "ask_on_escape"
10622   },
10623   {
10624     TYPE_SWITCH,
10625     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10626   },
10627   {
10628     TYPE_SWITCH,
10629     &setup.ask_on_game_over,                    "ask_on_game_over"
10630   },
10631   {
10632     TYPE_SWITCH,
10633     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10634   },
10635   {
10636     TYPE_SWITCH,
10637     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10638   },
10639   {
10640     TYPE_SWITCH,
10641     &setup.quick_switch,                        "quick_player_switch"
10642   },
10643   {
10644     TYPE_SWITCH,
10645     &setup.input_on_focus,                      "input_on_focus"
10646   },
10647   {
10648     TYPE_SWITCH,
10649     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10650   },
10651   {
10652     TYPE_SWITCH,
10653     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10654   },
10655   {
10656     TYPE_SWITCH,
10657     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10658   },
10659   {
10660     TYPE_SWITCH,
10661     &setup.game_speed_extended,                 "game_speed_extended"
10662   },
10663   {
10664     TYPE_INTEGER,
10665     &setup.game_frame_delay,                    "game_frame_delay"
10666   },
10667   {
10668     TYPE_SWITCH,
10669     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10670   },
10671   {
10672     TYPE_SWITCH,
10673     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10674   },
10675   {
10676     TYPE_SWITCH,
10677     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10678   },
10679   {
10680     TYPE_SWITCH3,
10681     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10682   },
10683   {
10684     TYPE_SWITCH,
10685     &setup.sp_show_border_elements,             "sp_show_border_elements"
10686   },
10687   {
10688     TYPE_SWITCH,
10689     &setup.small_game_graphics,                 "small_game_graphics"
10690   },
10691   {
10692     TYPE_SWITCH,
10693     &setup.show_load_save_buttons,              "show_load_save_buttons"
10694   },
10695   {
10696     TYPE_SWITCH,
10697     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10698   },
10699   {
10700     TYPE_STRING,
10701     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10702   },
10703   {
10704     TYPE_STRING,
10705     &setup.graphics_set,                        "graphics_set"
10706   },
10707   {
10708     TYPE_STRING,
10709     &setup.sounds_set,                          "sounds_set"
10710   },
10711   {
10712     TYPE_STRING,
10713     &setup.music_set,                           "music_set"
10714   },
10715   {
10716     TYPE_SWITCH3,
10717     &setup.override_level_graphics,             "override_level_graphics"
10718   },
10719   {
10720     TYPE_SWITCH3,
10721     &setup.override_level_sounds,               "override_level_sounds"
10722   },
10723   {
10724     TYPE_SWITCH3,
10725     &setup.override_level_music,                "override_level_music"
10726   },
10727   {
10728     TYPE_INTEGER,
10729     &setup.volume_simple,                       "volume_simple"
10730   },
10731   {
10732     TYPE_INTEGER,
10733     &setup.volume_loops,                        "volume_loops"
10734   },
10735   {
10736     TYPE_INTEGER,
10737     &setup.volume_music,                        "volume_music"
10738   },
10739   {
10740     TYPE_SWITCH,
10741     &setup.network_mode,                        "network_mode"
10742   },
10743   {
10744     TYPE_PLAYER,
10745     &setup.network_player_nr,                   "network_player"
10746   },
10747   {
10748     TYPE_STRING,
10749     &setup.network_server_hostname,             "network_server_hostname"
10750   },
10751   {
10752     TYPE_STRING,
10753     &setup.touch.control_type,                  "touch.control_type"
10754   },
10755   {
10756     TYPE_INTEGER,
10757     &setup.touch.move_distance,                 "touch.move_distance"
10758   },
10759   {
10760     TYPE_INTEGER,
10761     &setup.touch.drop_distance,                 "touch.drop_distance"
10762   },
10763   {
10764     TYPE_INTEGER,
10765     &setup.touch.transparency,                  "touch.transparency"
10766   },
10767   {
10768     TYPE_INTEGER,
10769     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10770   },
10771   {
10772     TYPE_INTEGER,
10773     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10774   },
10775   {
10776     TYPE_INTEGER,
10777     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10778   },
10779   {
10780     TYPE_INTEGER,
10781     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10782   },
10783   {
10784     TYPE_INTEGER,
10785     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10786   },
10787   {
10788     TYPE_INTEGER,
10789     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10790   },
10791   {
10792     TYPE_SWITCH,
10793     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10794   },
10795 };
10796
10797 static struct TokenInfo auto_setup_tokens[] =
10798 {
10799   {
10800     TYPE_INTEGER,
10801     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10802   },
10803 };
10804
10805 static struct TokenInfo server_setup_tokens[] =
10806 {
10807   {
10808     TYPE_STRING,
10809     &setup.player_uuid,                         "player_uuid"
10810   },
10811   {
10812     TYPE_INTEGER,
10813     &setup.player_version,                      "player_version"
10814   },
10815   {
10816     TYPE_SWITCH,
10817     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10818   },
10819   {
10820     TYPE_STRING,
10821     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10822   },
10823   {
10824     TYPE_STRING,
10825     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10826   },
10827   {
10828     TYPE_SWITCH,
10829     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10830   },
10831   {
10832     TYPE_SWITCH,
10833     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10834   },
10835   {
10836     TYPE_SWITCH,
10837     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10838   },
10839   {
10840     TYPE_SWITCH,
10841     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10842   },
10843   {
10844     TYPE_SWITCH,
10845     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10846   },
10847 };
10848
10849 static struct TokenInfo editor_setup_tokens[] =
10850 {
10851   {
10852     TYPE_SWITCH,
10853     &setup.editor.el_classic,                   "editor.el_classic"
10854   },
10855   {
10856     TYPE_SWITCH,
10857     &setup.editor.el_custom,                    "editor.el_custom"
10858   },
10859   {
10860     TYPE_SWITCH,
10861     &setup.editor.el_user_defined,              "editor.el_user_defined"
10862   },
10863   {
10864     TYPE_SWITCH,
10865     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10866   },
10867   {
10868     TYPE_SWITCH,
10869     &setup.editor.el_headlines,                 "editor.el_headlines"
10870   },
10871   {
10872     TYPE_SWITCH,
10873     &setup.editor.show_element_token,           "editor.show_element_token"
10874   },
10875   {
10876     TYPE_SWITCH,
10877     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10878   },
10879 };
10880
10881 static struct TokenInfo editor_cascade_setup_tokens[] =
10882 {
10883   {
10884     TYPE_SWITCH,
10885     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10886   },
10887   {
10888     TYPE_SWITCH,
10889     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10890   },
10891   {
10892     TYPE_SWITCH,
10893     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10894   },
10895   {
10896     TYPE_SWITCH,
10897     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10898   },
10899   {
10900     TYPE_SWITCH,
10901     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10902   },
10903   {
10904     TYPE_SWITCH,
10905     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10906   },
10907   {
10908     TYPE_SWITCH,
10909     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10910   },
10911   {
10912     TYPE_SWITCH,
10913     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10914   },
10915   {
10916     TYPE_SWITCH,
10917     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10918   },
10919   {
10920     TYPE_SWITCH,
10921     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10922   },
10923   {
10924     TYPE_SWITCH,
10925     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10926   },
10927   {
10928     TYPE_SWITCH,
10929     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10930   },
10931   {
10932     TYPE_SWITCH,
10933     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10934   },
10935   {
10936     TYPE_SWITCH,
10937     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10938   },
10939   {
10940     TYPE_SWITCH,
10941     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10942   },
10943   {
10944     TYPE_SWITCH,
10945     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10946   },
10947   {
10948     TYPE_SWITCH,
10949     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10950   },
10951   {
10952     TYPE_SWITCH,
10953     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10954   },
10955   {
10956     TYPE_SWITCH,
10957     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10958   },
10959   {
10960     TYPE_SWITCH,
10961     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10962   },
10963 };
10964
10965 static struct TokenInfo shortcut_setup_tokens[] =
10966 {
10967   {
10968     TYPE_KEY_X11,
10969     &setup.shortcut.save_game,                  "shortcut.save_game"
10970   },
10971   {
10972     TYPE_KEY_X11,
10973     &setup.shortcut.load_game,                  "shortcut.load_game"
10974   },
10975   {
10976     TYPE_KEY_X11,
10977     &setup.shortcut.restart_game,               "shortcut.restart_game"
10978   },
10979   {
10980     TYPE_KEY_X11,
10981     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10982   },
10983   {
10984     TYPE_KEY_X11,
10985     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10986   },
10987   {
10988     TYPE_KEY_X11,
10989     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10990   },
10991   {
10992     TYPE_KEY_X11,
10993     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10994   },
10995   {
10996     TYPE_KEY_X11,
10997     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10998   },
10999   {
11000     TYPE_KEY_X11,
11001     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11002   },
11003   {
11004     TYPE_KEY_X11,
11005     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11006   },
11007   {
11008     TYPE_KEY_X11,
11009     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11010   },
11011   {
11012     TYPE_KEY_X11,
11013     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11014   },
11015   {
11016     TYPE_KEY_X11,
11017     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11018   },
11019   {
11020     TYPE_KEY_X11,
11021     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11022   },
11023   {
11024     TYPE_KEY_X11,
11025     &setup.shortcut.tape_record,                "shortcut.tape_record"
11026   },
11027   {
11028     TYPE_KEY_X11,
11029     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11030   },
11031   {
11032     TYPE_KEY_X11,
11033     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11034   },
11035   {
11036     TYPE_KEY_X11,
11037     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11038   },
11039   {
11040     TYPE_KEY_X11,
11041     &setup.shortcut.sound_music,                "shortcut.sound_music"
11042   },
11043   {
11044     TYPE_KEY_X11,
11045     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11046   },
11047   {
11048     TYPE_KEY_X11,
11049     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11050   },
11051   {
11052     TYPE_KEY_X11,
11053     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11054   },
11055   {
11056     TYPE_KEY_X11,
11057     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11058   },
11059 };
11060
11061 static struct SetupInputInfo setup_input;
11062 static struct TokenInfo player_setup_tokens[] =
11063 {
11064   {
11065     TYPE_BOOLEAN,
11066     &setup_input.use_joystick,                  ".use_joystick"
11067   },
11068   {
11069     TYPE_STRING,
11070     &setup_input.joy.device_name,               ".joy.device_name"
11071   },
11072   {
11073     TYPE_INTEGER,
11074     &setup_input.joy.xleft,                     ".joy.xleft"
11075   },
11076   {
11077     TYPE_INTEGER,
11078     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11079   },
11080   {
11081     TYPE_INTEGER,
11082     &setup_input.joy.xright,                    ".joy.xright"
11083   },
11084   {
11085     TYPE_INTEGER,
11086     &setup_input.joy.yupper,                    ".joy.yupper"
11087   },
11088   {
11089     TYPE_INTEGER,
11090     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11091   },
11092   {
11093     TYPE_INTEGER,
11094     &setup_input.joy.ylower,                    ".joy.ylower"
11095   },
11096   {
11097     TYPE_INTEGER,
11098     &setup_input.joy.snap,                      ".joy.snap_field"
11099   },
11100   {
11101     TYPE_INTEGER,
11102     &setup_input.joy.drop,                      ".joy.place_bomb"
11103   },
11104   {
11105     TYPE_KEY_X11,
11106     &setup_input.key.left,                      ".key.move_left"
11107   },
11108   {
11109     TYPE_KEY_X11,
11110     &setup_input.key.right,                     ".key.move_right"
11111   },
11112   {
11113     TYPE_KEY_X11,
11114     &setup_input.key.up,                        ".key.move_up"
11115   },
11116   {
11117     TYPE_KEY_X11,
11118     &setup_input.key.down,                      ".key.move_down"
11119   },
11120   {
11121     TYPE_KEY_X11,
11122     &setup_input.key.snap,                      ".key.snap_field"
11123   },
11124   {
11125     TYPE_KEY_X11,
11126     &setup_input.key.drop,                      ".key.place_bomb"
11127   },
11128 };
11129
11130 static struct TokenInfo system_setup_tokens[] =
11131 {
11132   {
11133     TYPE_STRING,
11134     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11135   },
11136   {
11137     TYPE_STRING,
11138     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11139   },
11140   {
11141     TYPE_STRING,
11142     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11143   },
11144   {
11145     TYPE_INTEGER,
11146     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11147   },
11148 };
11149
11150 static struct TokenInfo internal_setup_tokens[] =
11151 {
11152   {
11153     TYPE_STRING,
11154     &setup.internal.program_title,              "program_title"
11155   },
11156   {
11157     TYPE_STRING,
11158     &setup.internal.program_version,            "program_version"
11159   },
11160   {
11161     TYPE_STRING,
11162     &setup.internal.program_author,             "program_author"
11163   },
11164   {
11165     TYPE_STRING,
11166     &setup.internal.program_email,              "program_email"
11167   },
11168   {
11169     TYPE_STRING,
11170     &setup.internal.program_website,            "program_website"
11171   },
11172   {
11173     TYPE_STRING,
11174     &setup.internal.program_copyright,          "program_copyright"
11175   },
11176   {
11177     TYPE_STRING,
11178     &setup.internal.program_company,            "program_company"
11179   },
11180   {
11181     TYPE_STRING,
11182     &setup.internal.program_icon_file,          "program_icon_file"
11183   },
11184   {
11185     TYPE_STRING,
11186     &setup.internal.default_graphics_set,       "default_graphics_set"
11187   },
11188   {
11189     TYPE_STRING,
11190     &setup.internal.default_sounds_set,         "default_sounds_set"
11191   },
11192   {
11193     TYPE_STRING,
11194     &setup.internal.default_music_set,          "default_music_set"
11195   },
11196   {
11197     TYPE_STRING,
11198     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11199   },
11200   {
11201     TYPE_STRING,
11202     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11203   },
11204   {
11205     TYPE_STRING,
11206     &setup.internal.fallback_music_file,        "fallback_music_file"
11207   },
11208   {
11209     TYPE_STRING,
11210     &setup.internal.default_level_series,       "default_level_series"
11211   },
11212   {
11213     TYPE_INTEGER,
11214     &setup.internal.default_window_width,       "default_window_width"
11215   },
11216   {
11217     TYPE_INTEGER,
11218     &setup.internal.default_window_height,      "default_window_height"
11219   },
11220   {
11221     TYPE_BOOLEAN,
11222     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11223   },
11224   {
11225     TYPE_BOOLEAN,
11226     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11227   },
11228   {
11229     TYPE_BOOLEAN,
11230     &setup.internal.create_user_levelset,       "create_user_levelset"
11231   },
11232   {
11233     TYPE_BOOLEAN,
11234     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11235   },
11236   {
11237     TYPE_BOOLEAN,
11238     &setup.internal.menu_game,                  "menu_game"
11239   },
11240   {
11241     TYPE_BOOLEAN,
11242     &setup.internal.menu_engines,               "menu_engines"
11243   },
11244   {
11245     TYPE_BOOLEAN,
11246     &setup.internal.menu_editor,                "menu_editor"
11247   },
11248   {
11249     TYPE_BOOLEAN,
11250     &setup.internal.menu_graphics,              "menu_graphics"
11251   },
11252   {
11253     TYPE_BOOLEAN,
11254     &setup.internal.menu_sound,                 "menu_sound"
11255   },
11256   {
11257     TYPE_BOOLEAN,
11258     &setup.internal.menu_artwork,               "menu_artwork"
11259   },
11260   {
11261     TYPE_BOOLEAN,
11262     &setup.internal.menu_input,                 "menu_input"
11263   },
11264   {
11265     TYPE_BOOLEAN,
11266     &setup.internal.menu_touch,                 "menu_touch"
11267   },
11268   {
11269     TYPE_BOOLEAN,
11270     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11271   },
11272   {
11273     TYPE_BOOLEAN,
11274     &setup.internal.menu_exit,                  "menu_exit"
11275   },
11276   {
11277     TYPE_BOOLEAN,
11278     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11279   },
11280   {
11281     TYPE_BOOLEAN,
11282     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11283   },
11284   {
11285     TYPE_BOOLEAN,
11286     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11287   },
11288   {
11289     TYPE_BOOLEAN,
11290     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11291   },
11292   {
11293     TYPE_BOOLEAN,
11294     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11295   },
11296   {
11297     TYPE_BOOLEAN,
11298     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11299   },
11300   {
11301     TYPE_BOOLEAN,
11302     &setup.internal.info_title,                 "info_title"
11303   },
11304   {
11305     TYPE_BOOLEAN,
11306     &setup.internal.info_elements,              "info_elements"
11307   },
11308   {
11309     TYPE_BOOLEAN,
11310     &setup.internal.info_music,                 "info_music"
11311   },
11312   {
11313     TYPE_BOOLEAN,
11314     &setup.internal.info_credits,               "info_credits"
11315   },
11316   {
11317     TYPE_BOOLEAN,
11318     &setup.internal.info_program,               "info_program"
11319   },
11320   {
11321     TYPE_BOOLEAN,
11322     &setup.internal.info_version,               "info_version"
11323   },
11324   {
11325     TYPE_BOOLEAN,
11326     &setup.internal.info_levelset,              "info_levelset"
11327   },
11328   {
11329     TYPE_BOOLEAN,
11330     &setup.internal.info_exit,                  "info_exit"
11331   },
11332 };
11333
11334 static struct TokenInfo debug_setup_tokens[] =
11335 {
11336   {
11337     TYPE_INTEGER,
11338     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11339   },
11340   {
11341     TYPE_INTEGER,
11342     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11343   },
11344   {
11345     TYPE_INTEGER,
11346     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11347   },
11348   {
11349     TYPE_INTEGER,
11350     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11351   },
11352   {
11353     TYPE_INTEGER,
11354     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11355   },
11356   {
11357     TYPE_INTEGER,
11358     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11359   },
11360   {
11361     TYPE_INTEGER,
11362     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11363   },
11364   {
11365     TYPE_INTEGER,
11366     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11367   },
11368   {
11369     TYPE_INTEGER,
11370     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11371   },
11372   {
11373     TYPE_INTEGER,
11374     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11375   },
11376   {
11377     TYPE_KEY_X11,
11378     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11379   },
11380   {
11381     TYPE_KEY_X11,
11382     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11383   },
11384   {
11385     TYPE_KEY_X11,
11386     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11387   },
11388   {
11389     TYPE_KEY_X11,
11390     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11391   },
11392   {
11393     TYPE_KEY_X11,
11394     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11395   },
11396   {
11397     TYPE_KEY_X11,
11398     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11399   },
11400   {
11401     TYPE_KEY_X11,
11402     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11403   },
11404   {
11405     TYPE_KEY_X11,
11406     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11407   },
11408   {
11409     TYPE_KEY_X11,
11410     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11411   },
11412   {
11413     TYPE_KEY_X11,
11414     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11415   },
11416   {
11417     TYPE_BOOLEAN,
11418     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11419   {
11420     TYPE_BOOLEAN,
11421     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11422   },
11423   {
11424     TYPE_BOOLEAN,
11425     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11426   },
11427   {
11428     TYPE_SWITCH3,
11429     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11430   },
11431   {
11432     TYPE_INTEGER,
11433     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11434   },
11435 };
11436
11437 static struct TokenInfo options_setup_tokens[] =
11438 {
11439   {
11440     TYPE_BOOLEAN,
11441     &setup.options.verbose,                     "options.verbose"
11442   },
11443   {
11444     TYPE_BOOLEAN,
11445     &setup.options.debug,                       "options.debug"
11446   },
11447   {
11448     TYPE_STRING,
11449     &setup.options.debug_mode,                  "options.debug_mode"
11450   },
11451 };
11452
11453 static void setSetupInfoToDefaults(struct SetupInfo *si)
11454 {
11455   int i;
11456
11457   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11458
11459   si->multiple_users = TRUE;
11460
11461   si->sound = TRUE;
11462   si->sound_loops = TRUE;
11463   si->sound_music = TRUE;
11464   si->sound_simple = TRUE;
11465   si->toons = TRUE;
11466   si->global_animations = TRUE;
11467   si->scroll_delay = TRUE;
11468   si->forced_scroll_delay = FALSE;
11469   si->scroll_delay_value = STD_SCROLL_DELAY;
11470   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11471   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11472   si->fade_screens = TRUE;
11473   si->autorecord = TRUE;
11474   si->autorecord_after_replay = TRUE;
11475   si->auto_pause_on_start = FALSE;
11476   si->show_titlescreen = TRUE;
11477   si->quick_doors = FALSE;
11478   si->team_mode = FALSE;
11479   si->handicap = TRUE;
11480   si->skip_levels = TRUE;
11481   si->increment_levels = TRUE;
11482   si->auto_play_next_level = TRUE;
11483   si->count_score_after_game = TRUE;
11484   si->show_scores_after_game = TRUE;
11485   si->time_limit = TRUE;
11486   si->fullscreen = FALSE;
11487   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11488   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11489   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11490   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11491   si->ask_on_escape = TRUE;
11492   si->ask_on_escape_editor = TRUE;
11493   si->ask_on_game_over = TRUE;
11494   si->ask_on_quit_game = TRUE;
11495   si->ask_on_quit_program = TRUE;
11496   si->quick_switch = FALSE;
11497   si->input_on_focus = FALSE;
11498   si->prefer_aga_graphics = TRUE;
11499   si->prefer_lowpass_sounds = FALSE;
11500   si->prefer_extra_panel_items = TRUE;
11501   si->game_speed_extended = FALSE;
11502   si->game_frame_delay = GAME_FRAME_DELAY;
11503   si->bd_skip_uncovering = FALSE;
11504   si->bd_skip_hatching = FALSE;
11505   si->bd_scroll_delay = TRUE;
11506   si->bd_smooth_movements = AUTO;
11507   si->sp_show_border_elements = FALSE;
11508   si->small_game_graphics = FALSE;
11509   si->show_load_save_buttons = FALSE;
11510   si->show_undo_redo_buttons = FALSE;
11511   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11512
11513   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11514   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11515   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11516
11517   si->override_level_graphics = FALSE;
11518   si->override_level_sounds = FALSE;
11519   si->override_level_music = FALSE;
11520
11521   si->volume_simple = 100;              // percent
11522   si->volume_loops = 100;               // percent
11523   si->volume_music = 100;               // percent
11524
11525   si->network_mode = FALSE;
11526   si->network_player_nr = 0;            // first player
11527   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11528
11529   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11530   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11531   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11532   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11533   si->touch.draw_outlined = TRUE;
11534   si->touch.draw_pressed = TRUE;
11535
11536   for (i = 0; i < 2; i++)
11537   {
11538     char *default_grid_button[6][2] =
11539     {
11540       { "      ", "  ^^  " },
11541       { "      ", "  ^^  " },
11542       { "      ", "<<  >>" },
11543       { "      ", "<<  >>" },
11544       { "111222", "  vv  " },
11545       { "111222", "  vv  " }
11546     };
11547     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11548     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11549     int min_xsize = MIN(6, grid_xsize);
11550     int min_ysize = MIN(6, grid_ysize);
11551     int startx = grid_xsize - min_xsize;
11552     int starty = grid_ysize - min_ysize;
11553     int x, y;
11554
11555     // virtual buttons grid can only be set to defaults if video is initialized
11556     // (this will be repeated if virtual buttons are not loaded from setup file)
11557     if (video.initialized)
11558     {
11559       si->touch.grid_xsize[i] = grid_xsize;
11560       si->touch.grid_ysize[i] = grid_ysize;
11561     }
11562     else
11563     {
11564       si->touch.grid_xsize[i] = -1;
11565       si->touch.grid_ysize[i] = -1;
11566     }
11567
11568     for (x = 0; x < MAX_GRID_XSIZE; x++)
11569       for (y = 0; y < MAX_GRID_YSIZE; y++)
11570         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11571
11572     for (x = 0; x < min_xsize; x++)
11573       for (y = 0; y < min_ysize; y++)
11574         si->touch.grid_button[i][x][starty + y] =
11575           default_grid_button[y][0][x];
11576
11577     for (x = 0; x < min_xsize; x++)
11578       for (y = 0; y < min_ysize; y++)
11579         si->touch.grid_button[i][startx + x][starty + y] =
11580           default_grid_button[y][1][x];
11581   }
11582
11583   si->touch.grid_initialized            = video.initialized;
11584
11585   si->touch.overlay_buttons             = FALSE;
11586
11587   si->editor.el_boulderdash             = TRUE;
11588   si->editor.el_boulderdash_native      = TRUE;
11589   si->editor.el_boulderdash_effects     = TRUE;
11590   si->editor.el_emerald_mine            = TRUE;
11591   si->editor.el_emerald_mine_club       = TRUE;
11592   si->editor.el_more                    = TRUE;
11593   si->editor.el_sokoban                 = TRUE;
11594   si->editor.el_supaplex                = TRUE;
11595   si->editor.el_diamond_caves           = TRUE;
11596   si->editor.el_dx_boulderdash          = TRUE;
11597
11598   si->editor.el_mirror_magic            = TRUE;
11599   si->editor.el_deflektor               = TRUE;
11600
11601   si->editor.el_chars                   = TRUE;
11602   si->editor.el_steel_chars             = TRUE;
11603
11604   si->editor.el_classic                 = TRUE;
11605   si->editor.el_custom                  = TRUE;
11606
11607   si->editor.el_user_defined            = FALSE;
11608   si->editor.el_dynamic                 = TRUE;
11609
11610   si->editor.el_headlines               = TRUE;
11611
11612   si->editor.show_element_token         = FALSE;
11613
11614   si->editor.show_read_only_warning     = TRUE;
11615
11616   si->editor.use_template_for_new_levels = TRUE;
11617
11618   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11619   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11620   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11621   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11622   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11623
11624   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11625   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11626   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11627   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11628   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11629
11630   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11631   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11632   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11633   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11634   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11635   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11636
11637   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11638   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11639   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11640
11641   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11642   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11643   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11644   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11645
11646   for (i = 0; i < MAX_PLAYERS; i++)
11647   {
11648     si->input[i].use_joystick = FALSE;
11649     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11650     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11651     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11652     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11653     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11654     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11655     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11656     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11657     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11658     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11659     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11660     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11661     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11662     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11663     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11664   }
11665
11666   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11667   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11668   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11669   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11670
11671   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11672   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11673   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11674   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11675   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11676   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11677   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11678
11679   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11680
11681   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11682   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11683   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11684
11685   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11686   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11687   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11688
11689   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11690   si->internal.choose_from_top_leveldir = FALSE;
11691   si->internal.show_scaling_in_title = TRUE;
11692   si->internal.create_user_levelset = TRUE;
11693   si->internal.info_screens_from_main = FALSE;
11694
11695   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11696   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11697
11698   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11699   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11700   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11701   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11702   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11703   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11704   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11705   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11706   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11707   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11708
11709   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11710   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11711   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11712   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11713   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11714   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11715   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11716   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11717   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11718   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11719
11720   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11721   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11722
11723   si->debug.show_frames_per_second = FALSE;
11724
11725   si->debug.xsn_mode = AUTO;
11726   si->debug.xsn_percent = 0;
11727
11728   si->options.verbose = FALSE;
11729   si->options.debug = FALSE;
11730   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11731
11732 #if defined(PLATFORM_ANDROID)
11733   si->fullscreen = TRUE;
11734   si->touch.overlay_buttons = TRUE;
11735 #endif
11736
11737   setHideSetupEntry(&setup.debug.xsn_mode);
11738 }
11739
11740 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11741 {
11742   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11743 }
11744
11745 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11746 {
11747   si->player_uuid = NULL;       // (will be set later)
11748   si->player_version = 1;       // (will be set later)
11749
11750   si->use_api_server = TRUE;
11751   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11752   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11753   si->ask_for_uploading_tapes = TRUE;
11754   si->ask_for_remaining_tapes = FALSE;
11755   si->provide_uploading_tapes = TRUE;
11756   si->ask_for_using_api_server = TRUE;
11757   si->has_remaining_tapes = FALSE;
11758 }
11759
11760 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11761 {
11762   si->editor_cascade.el_bd              = TRUE;
11763   si->editor_cascade.el_bd_native       = TRUE;
11764   si->editor_cascade.el_bd_effects      = FALSE;
11765   si->editor_cascade.el_em              = TRUE;
11766   si->editor_cascade.el_emc             = TRUE;
11767   si->editor_cascade.el_rnd             = TRUE;
11768   si->editor_cascade.el_sb              = TRUE;
11769   si->editor_cascade.el_sp              = TRUE;
11770   si->editor_cascade.el_dc              = TRUE;
11771   si->editor_cascade.el_dx              = TRUE;
11772
11773   si->editor_cascade.el_mm              = TRUE;
11774   si->editor_cascade.el_df              = TRUE;
11775
11776   si->editor_cascade.el_chars           = FALSE;
11777   si->editor_cascade.el_steel_chars     = FALSE;
11778   si->editor_cascade.el_ce              = FALSE;
11779   si->editor_cascade.el_ge              = FALSE;
11780   si->editor_cascade.el_es              = FALSE;
11781   si->editor_cascade.el_ref             = FALSE;
11782   si->editor_cascade.el_user            = FALSE;
11783   si->editor_cascade.el_dynamic         = FALSE;
11784 }
11785
11786 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11787
11788 static char *getHideSetupToken(void *setup_value)
11789 {
11790   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11791
11792   if (setup_value != NULL)
11793     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11794
11795   return hide_setup_token;
11796 }
11797
11798 void setHideSetupEntry(void *setup_value)
11799 {
11800   char *hide_setup_token = getHideSetupToken(setup_value);
11801
11802   if (hide_setup_hash == NULL)
11803     hide_setup_hash = newSetupFileHash();
11804
11805   if (setup_value != NULL)
11806     setHashEntry(hide_setup_hash, hide_setup_token, "");
11807 }
11808
11809 void removeHideSetupEntry(void *setup_value)
11810 {
11811   char *hide_setup_token = getHideSetupToken(setup_value);
11812
11813   if (setup_value != NULL)
11814     removeHashEntry(hide_setup_hash, hide_setup_token);
11815 }
11816
11817 boolean hideSetupEntry(void *setup_value)
11818 {
11819   char *hide_setup_token = getHideSetupToken(setup_value);
11820
11821   return (setup_value != NULL &&
11822           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11823 }
11824
11825 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11826                                       struct TokenInfo *token_info,
11827                                       int token_nr, char *token_text)
11828 {
11829   char *token_hide_text = getStringCat2(token_text, ".hide");
11830   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11831
11832   // set the value of this setup option in the setup option structure
11833   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11834
11835   // check if this setup option should be hidden in the setup menu
11836   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11837     setHideSetupEntry(token_info[token_nr].value);
11838
11839   free(token_hide_text);
11840 }
11841
11842 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11843                                       struct TokenInfo *token_info,
11844                                       int token_nr)
11845 {
11846   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11847                             token_info[token_nr].text);
11848 }
11849
11850 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11851 {
11852   int i, pnr;
11853
11854   if (!setup_file_hash)
11855     return;
11856
11857   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11858     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11859
11860   setup.touch.grid_initialized = TRUE;
11861   for (i = 0; i < 2; i++)
11862   {
11863     int grid_xsize = setup.touch.grid_xsize[i];
11864     int grid_ysize = setup.touch.grid_ysize[i];
11865     int x, y;
11866
11867     // if virtual buttons are not loaded from setup file, repeat initializing
11868     // virtual buttons grid with default values later when video is initialized
11869     if (grid_xsize == -1 ||
11870         grid_ysize == -1)
11871     {
11872       setup.touch.grid_initialized = FALSE;
11873
11874       continue;
11875     }
11876
11877     for (y = 0; y < grid_ysize; y++)
11878     {
11879       char token_string[MAX_LINE_LEN];
11880
11881       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11882
11883       char *value_string = getHashEntry(setup_file_hash, token_string);
11884
11885       if (value_string == NULL)
11886         continue;
11887
11888       for (x = 0; x < grid_xsize; x++)
11889       {
11890         char c = value_string[x];
11891
11892         setup.touch.grid_button[i][x][y] =
11893           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11894       }
11895     }
11896   }
11897
11898   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11899     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11900
11901   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11902     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11903
11904   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11905   {
11906     char prefix[30];
11907
11908     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11909
11910     setup_input = setup.input[pnr];
11911     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11912     {
11913       char full_token[100];
11914
11915       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11916       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11917                                 full_token);
11918     }
11919     setup.input[pnr] = setup_input;
11920   }
11921
11922   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11923     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11924
11925   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11926     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11927
11928   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11929     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11930
11931   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11932     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11933
11934   setHideRelatedSetupEntries();
11935 }
11936
11937 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11938 {
11939   int i;
11940
11941   if (!setup_file_hash)
11942     return;
11943
11944   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11945     setSetupInfo(auto_setup_tokens, i,
11946                  getHashEntry(setup_file_hash,
11947                               auto_setup_tokens[i].text));
11948 }
11949
11950 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11951 {
11952   int i;
11953
11954   if (!setup_file_hash)
11955     return;
11956
11957   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11958     setSetupInfo(server_setup_tokens, i,
11959                  getHashEntry(setup_file_hash,
11960                               server_setup_tokens[i].text));
11961 }
11962
11963 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11964 {
11965   int i;
11966
11967   if (!setup_file_hash)
11968     return;
11969
11970   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11971     setSetupInfo(editor_cascade_setup_tokens, i,
11972                  getHashEntry(setup_file_hash,
11973                               editor_cascade_setup_tokens[i].text));
11974 }
11975
11976 void LoadUserNames(void)
11977 {
11978   int last_user_nr = user.nr;
11979   int i;
11980
11981   if (global.user_names != NULL)
11982   {
11983     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11984       checked_free(global.user_names[i]);
11985
11986     checked_free(global.user_names);
11987   }
11988
11989   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11990
11991   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11992   {
11993     user.nr = i;
11994
11995     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11996
11997     if (setup_file_hash)
11998     {
11999       char *player_name = getHashEntry(setup_file_hash, "player_name");
12000
12001       global.user_names[i] = getFixedUserName(player_name);
12002
12003       freeSetupFileHash(setup_file_hash);
12004     }
12005
12006     if (global.user_names[i] == NULL)
12007       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12008   }
12009
12010   user.nr = last_user_nr;
12011 }
12012
12013 void LoadSetupFromFilename(char *filename)
12014 {
12015   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12016
12017   if (setup_file_hash)
12018   {
12019     decodeSetupFileHash_Default(setup_file_hash);
12020
12021     freeSetupFileHash(setup_file_hash);
12022   }
12023   else
12024   {
12025     Debug("setup", "using default setup values");
12026   }
12027 }
12028
12029 static void LoadSetup_SpecialPostProcessing(void)
12030 {
12031   char *player_name_new;
12032
12033   // needed to work around problems with fixed length strings
12034   player_name_new = getFixedUserName(setup.player_name);
12035   free(setup.player_name);
12036   setup.player_name = player_name_new;
12037
12038   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12039   if (setup.scroll_delay == FALSE)
12040   {
12041     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12042     setup.scroll_delay = TRUE;                  // now always "on"
12043   }
12044
12045   // make sure that scroll delay value stays inside valid range
12046   setup.scroll_delay_value =
12047     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12048 }
12049
12050 void LoadSetup_Default(void)
12051 {
12052   char *filename;
12053
12054   // always start with reliable default values
12055   setSetupInfoToDefaults(&setup);
12056
12057   // try to load setup values from default setup file
12058   filename = getDefaultSetupFilename();
12059
12060   if (fileExists(filename))
12061     LoadSetupFromFilename(filename);
12062
12063   // try to load setup values from platform setup file
12064   filename = getPlatformSetupFilename();
12065
12066   if (fileExists(filename))
12067     LoadSetupFromFilename(filename);
12068
12069   // try to load setup values from user setup file
12070   filename = getSetupFilename();
12071
12072   LoadSetupFromFilename(filename);
12073
12074   LoadSetup_SpecialPostProcessing();
12075 }
12076
12077 void LoadSetup_AutoSetup(void)
12078 {
12079   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12080   SetupFileHash *setup_file_hash = NULL;
12081
12082   // always start with reliable default values
12083   setSetupInfoToDefaults_AutoSetup(&setup);
12084
12085   setup_file_hash = loadSetupFileHash(filename);
12086
12087   if (setup_file_hash)
12088   {
12089     decodeSetupFileHash_AutoSetup(setup_file_hash);
12090
12091     freeSetupFileHash(setup_file_hash);
12092   }
12093
12094   free(filename);
12095 }
12096
12097 void LoadSetup_ServerSetup(void)
12098 {
12099   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12100   SetupFileHash *setup_file_hash = NULL;
12101
12102   // always start with reliable default values
12103   setSetupInfoToDefaults_ServerSetup(&setup);
12104
12105   setup_file_hash = loadSetupFileHash(filename);
12106
12107   if (setup_file_hash)
12108   {
12109     decodeSetupFileHash_ServerSetup(setup_file_hash);
12110
12111     freeSetupFileHash(setup_file_hash);
12112   }
12113
12114   free(filename);
12115
12116   if (setup.player_uuid == NULL)
12117   {
12118     // player UUID does not yet exist in setup file
12119     setup.player_uuid = getStringCopy(getUUID());
12120     setup.player_version = 2;
12121
12122     SaveSetup_ServerSetup();
12123   }
12124 }
12125
12126 void LoadSetup_EditorCascade(void)
12127 {
12128   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12129   SetupFileHash *setup_file_hash = NULL;
12130
12131   // always start with reliable default values
12132   setSetupInfoToDefaults_EditorCascade(&setup);
12133
12134   setup_file_hash = loadSetupFileHash(filename);
12135
12136   if (setup_file_hash)
12137   {
12138     decodeSetupFileHash_EditorCascade(setup_file_hash);
12139
12140     freeSetupFileHash(setup_file_hash);
12141   }
12142
12143   free(filename);
12144 }
12145
12146 void LoadSetup(void)
12147 {
12148   LoadSetup_Default();
12149   LoadSetup_AutoSetup();
12150   LoadSetup_ServerSetup();
12151   LoadSetup_EditorCascade();
12152 }
12153
12154 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12155                                            char *mapping_line)
12156 {
12157   char mapping_guid[MAX_LINE_LEN];
12158   char *mapping_start, *mapping_end;
12159
12160   // get GUID from game controller mapping line: copy complete line
12161   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12162   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12163
12164   // get GUID from game controller mapping line: cut after GUID part
12165   mapping_start = strchr(mapping_guid, ',');
12166   if (mapping_start != NULL)
12167     *mapping_start = '\0';
12168
12169   // cut newline from game controller mapping line
12170   mapping_end = strchr(mapping_line, '\n');
12171   if (mapping_end != NULL)
12172     *mapping_end = '\0';
12173
12174   // add mapping entry to game controller mappings hash
12175   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12176 }
12177
12178 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12179                                                  char *filename)
12180 {
12181   FILE *file;
12182
12183   if (!(file = fopen(filename, MODE_READ)))
12184   {
12185     Warn("cannot read game controller mappings file '%s'", filename);
12186
12187     return;
12188   }
12189
12190   while (!feof(file))
12191   {
12192     char line[MAX_LINE_LEN];
12193
12194     if (!fgets(line, MAX_LINE_LEN, file))
12195       break;
12196
12197     addGameControllerMappingToHash(mappings_hash, line);
12198   }
12199
12200   fclose(file);
12201 }
12202
12203 void SaveSetup_Default(void)
12204 {
12205   char *filename = getSetupFilename();
12206   FILE *file;
12207   int i, pnr;
12208
12209   InitUserDataDirectory();
12210
12211   if (!(file = fopen(filename, MODE_WRITE)))
12212   {
12213     Warn("cannot write setup file '%s'", filename);
12214
12215     return;
12216   }
12217
12218   fprintFileHeader(file, SETUP_FILENAME);
12219
12220   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12221   {
12222     // just to make things nicer :)
12223     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12224         global_setup_tokens[i].value == &setup.sound                    ||
12225         global_setup_tokens[i].value == &setup.graphics_set             ||
12226         global_setup_tokens[i].value == &setup.volume_simple            ||
12227         global_setup_tokens[i].value == &setup.network_mode             ||
12228         global_setup_tokens[i].value == &setup.touch.control_type       ||
12229         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12230         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12231       fprintf(file, "\n");
12232
12233     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12234   }
12235
12236   for (i = 0; i < 2; i++)
12237   {
12238     int grid_xsize = setup.touch.grid_xsize[i];
12239     int grid_ysize = setup.touch.grid_ysize[i];
12240     int x, y;
12241
12242     fprintf(file, "\n");
12243
12244     for (y = 0; y < grid_ysize; y++)
12245     {
12246       char token_string[MAX_LINE_LEN];
12247       char value_string[MAX_LINE_LEN];
12248
12249       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12250
12251       for (x = 0; x < grid_xsize; x++)
12252       {
12253         char c = setup.touch.grid_button[i][x][y];
12254
12255         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12256       }
12257
12258       value_string[grid_xsize] = '\0';
12259
12260       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12261     }
12262   }
12263
12264   fprintf(file, "\n");
12265   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12266     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12267
12268   fprintf(file, "\n");
12269   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12270     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12271
12272   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12273   {
12274     char prefix[30];
12275
12276     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12277     fprintf(file, "\n");
12278
12279     setup_input = setup.input[pnr];
12280     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12281       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12282   }
12283
12284   fprintf(file, "\n");
12285   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12286     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12287
12288   // (internal setup values not saved to user setup file)
12289
12290   fprintf(file, "\n");
12291   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12292     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12293         setup.debug.xsn_mode != AUTO)
12294       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12295
12296   fprintf(file, "\n");
12297   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12298     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12299
12300   fclose(file);
12301
12302   SetFilePermissions(filename, PERMS_PRIVATE);
12303 }
12304
12305 void SaveSetup_AutoSetup(void)
12306 {
12307   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12308   FILE *file;
12309   int i;
12310
12311   InitUserDataDirectory();
12312
12313   if (!(file = fopen(filename, MODE_WRITE)))
12314   {
12315     Warn("cannot write auto setup file '%s'", filename);
12316
12317     free(filename);
12318
12319     return;
12320   }
12321
12322   fprintFileHeader(file, AUTOSETUP_FILENAME);
12323
12324   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12325     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12326
12327   fclose(file);
12328
12329   SetFilePermissions(filename, PERMS_PRIVATE);
12330
12331   free(filename);
12332 }
12333
12334 void SaveSetup_ServerSetup(void)
12335 {
12336   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12337   FILE *file;
12338   int i;
12339
12340   InitUserDataDirectory();
12341
12342   if (!(file = fopen(filename, MODE_WRITE)))
12343   {
12344     Warn("cannot write server setup file '%s'", filename);
12345
12346     free(filename);
12347
12348     return;
12349   }
12350
12351   fprintFileHeader(file, SERVERSETUP_FILENAME);
12352
12353   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12354   {
12355     // just to make things nicer :)
12356     if (server_setup_tokens[i].value == &setup.use_api_server)
12357       fprintf(file, "\n");
12358
12359     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12360   }
12361
12362   fclose(file);
12363
12364   SetFilePermissions(filename, PERMS_PRIVATE);
12365
12366   free(filename);
12367 }
12368
12369 void SaveSetup_EditorCascade(void)
12370 {
12371   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12372   FILE *file;
12373   int i;
12374
12375   InitUserDataDirectory();
12376
12377   if (!(file = fopen(filename, MODE_WRITE)))
12378   {
12379     Warn("cannot write editor cascade state file '%s'", filename);
12380
12381     free(filename);
12382
12383     return;
12384   }
12385
12386   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12387
12388   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12389     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12390
12391   fclose(file);
12392
12393   SetFilePermissions(filename, PERMS_PRIVATE);
12394
12395   free(filename);
12396 }
12397
12398 void SaveSetup(void)
12399 {
12400   SaveSetup_Default();
12401   SaveSetup_AutoSetup();
12402   SaveSetup_ServerSetup();
12403   SaveSetup_EditorCascade();
12404 }
12405
12406 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12407                                                   char *filename)
12408 {
12409   FILE *file;
12410
12411   if (!(file = fopen(filename, MODE_WRITE)))
12412   {
12413     Warn("cannot write game controller mappings file '%s'", filename);
12414
12415     return;
12416   }
12417
12418   BEGIN_HASH_ITERATION(mappings_hash, itr)
12419   {
12420     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12421   }
12422   END_HASH_ITERATION(mappings_hash, itr)
12423
12424   fclose(file);
12425 }
12426
12427 void SaveSetup_AddGameControllerMapping(char *mapping)
12428 {
12429   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12430   SetupFileHash *mappings_hash = newSetupFileHash();
12431
12432   InitUserDataDirectory();
12433
12434   // load existing personal game controller mappings
12435   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12436
12437   // add new mapping to personal game controller mappings
12438   addGameControllerMappingToHash(mappings_hash, mapping);
12439
12440   // save updated personal game controller mappings
12441   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12442
12443   freeSetupFileHash(mappings_hash);
12444   free(filename);
12445 }
12446
12447 void LoadCustomElementDescriptions(void)
12448 {
12449   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12450   SetupFileHash *setup_file_hash;
12451   int i;
12452
12453   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12454   {
12455     if (element_info[i].custom_description != NULL)
12456     {
12457       free(element_info[i].custom_description);
12458       element_info[i].custom_description = NULL;
12459     }
12460   }
12461
12462   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12463     return;
12464
12465   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12466   {
12467     char *token = getStringCat2(element_info[i].token_name, ".name");
12468     char *value = getHashEntry(setup_file_hash, token);
12469
12470     if (value != NULL)
12471       element_info[i].custom_description = getStringCopy(value);
12472
12473     free(token);
12474   }
12475
12476   freeSetupFileHash(setup_file_hash);
12477 }
12478
12479 static int getElementFromToken(char *token)
12480 {
12481   char *value = getHashEntry(element_token_hash, token);
12482
12483   if (value != NULL)
12484     return atoi(value);
12485
12486   Warn("unknown element token '%s'", token);
12487
12488   return EL_UNDEFINED;
12489 }
12490
12491 void FreeGlobalAnimEventInfo(void)
12492 {
12493   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12494
12495   if (gaei->event_list == NULL)
12496     return;
12497
12498   int i;
12499
12500   for (i = 0; i < gaei->num_event_lists; i++)
12501   {
12502     checked_free(gaei->event_list[i]->event_value);
12503     checked_free(gaei->event_list[i]);
12504   }
12505
12506   checked_free(gaei->event_list);
12507
12508   gaei->event_list = NULL;
12509   gaei->num_event_lists = 0;
12510 }
12511
12512 static int AddGlobalAnimEventList(void)
12513 {
12514   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12515   int list_pos = gaei->num_event_lists++;
12516
12517   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12518                                      sizeof(struct GlobalAnimEventListInfo *));
12519
12520   gaei->event_list[list_pos] =
12521     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12522
12523   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12524
12525   gaeli->event_value = NULL;
12526   gaeli->num_event_values = 0;
12527
12528   return list_pos;
12529 }
12530
12531 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12532 {
12533   // do not add empty global animation events
12534   if (event_value == ANIM_EVENT_NONE)
12535     return list_pos;
12536
12537   // if list position is undefined, create new list
12538   if (list_pos == ANIM_EVENT_UNDEFINED)
12539     list_pos = AddGlobalAnimEventList();
12540
12541   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12542   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12543   int value_pos = gaeli->num_event_values++;
12544
12545   gaeli->event_value = checked_realloc(gaeli->event_value,
12546                                        gaeli->num_event_values * sizeof(int *));
12547
12548   gaeli->event_value[value_pos] = event_value;
12549
12550   return list_pos;
12551 }
12552
12553 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12554 {
12555   if (list_pos == ANIM_EVENT_UNDEFINED)
12556     return ANIM_EVENT_NONE;
12557
12558   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12559   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12560
12561   return gaeli->event_value[value_pos];
12562 }
12563
12564 int GetGlobalAnimEventValueCount(int list_pos)
12565 {
12566   if (list_pos == ANIM_EVENT_UNDEFINED)
12567     return 0;
12568
12569   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12570   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12571
12572   return gaeli->num_event_values;
12573 }
12574
12575 // This function checks if a string <s> of the format "string1, string2, ..."
12576 // exactly contains a string <s_contained>.
12577
12578 static boolean string_has_parameter(char *s, char *s_contained)
12579 {
12580   char *substring;
12581
12582   if (s == NULL || s_contained == NULL)
12583     return FALSE;
12584
12585   if (strlen(s_contained) > strlen(s))
12586     return FALSE;
12587
12588   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12589   {
12590     char next_char = s[strlen(s_contained)];
12591
12592     // check if next character is delimiter or whitespace
12593     if (next_char == ',' || next_char == '\0' ||
12594         next_char == ' ' || next_char == '\t')
12595       return TRUE;
12596   }
12597
12598   // check if string contains another parameter string after a comma
12599   substring = strchr(s, ',');
12600   if (substring == NULL)        // string does not contain a comma
12601     return FALSE;
12602
12603   // advance string pointer to next character after the comma
12604   substring++;
12605
12606   // skip potential whitespaces after the comma
12607   while (*substring == ' ' || *substring == '\t')
12608     substring++;
12609
12610   return string_has_parameter(substring, s_contained);
12611 }
12612
12613 static int get_anim_parameter_value_ce(char *s)
12614 {
12615   char *s_ptr = s;
12616   char *pattern_1 = "ce_change:custom_";
12617   char *pattern_2 = ".page_";
12618   int pattern_1_len = strlen(pattern_1);
12619   char *matching_char = strstr(s_ptr, pattern_1);
12620   int result = ANIM_EVENT_NONE;
12621
12622   if (matching_char == NULL)
12623     return ANIM_EVENT_NONE;
12624
12625   result = ANIM_EVENT_CE_CHANGE;
12626
12627   s_ptr = matching_char + pattern_1_len;
12628
12629   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12630   if (*s_ptr >= '0' && *s_ptr <= '9')
12631   {
12632     int gic_ce_nr = (*s_ptr++ - '0');
12633
12634     if (*s_ptr >= '0' && *s_ptr <= '9')
12635     {
12636       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12637
12638       if (*s_ptr >= '0' && *s_ptr <= '9')
12639         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12640     }
12641
12642     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12643       return ANIM_EVENT_NONE;
12644
12645     // custom element stored as 0 to 255
12646     gic_ce_nr--;
12647
12648     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12649   }
12650   else
12651   {
12652     // invalid custom element number specified
12653
12654     return ANIM_EVENT_NONE;
12655   }
12656
12657   // check for change page number ("page_X" or "page_XX") (optional)
12658   if (strPrefix(s_ptr, pattern_2))
12659   {
12660     s_ptr += strlen(pattern_2);
12661
12662     if (*s_ptr >= '0' && *s_ptr <= '9')
12663     {
12664       int gic_page_nr = (*s_ptr++ - '0');
12665
12666       if (*s_ptr >= '0' && *s_ptr <= '9')
12667         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12668
12669       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12670         return ANIM_EVENT_NONE;
12671
12672       // change page stored as 1 to 32 (0 means "all change pages")
12673
12674       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12675     }
12676     else
12677     {
12678       // invalid animation part number specified
12679
12680       return ANIM_EVENT_NONE;
12681     }
12682   }
12683
12684   // discard result if next character is neither delimiter nor whitespace
12685   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12686         *s_ptr == ' ' || *s_ptr == '\t'))
12687     return ANIM_EVENT_NONE;
12688
12689   return result;
12690 }
12691
12692 static int get_anim_parameter_value(char *s)
12693 {
12694   int event_value[] =
12695   {
12696     ANIM_EVENT_CLICK,
12697     ANIM_EVENT_INIT,
12698     ANIM_EVENT_START,
12699     ANIM_EVENT_END,
12700     ANIM_EVENT_POST
12701   };
12702   char *pattern_1[] =
12703   {
12704     "click:anim_",
12705     "init:anim_",
12706     "start:anim_",
12707     "end:anim_",
12708     "post:anim_"
12709   };
12710   char *pattern_2 = ".part_";
12711   char *matching_char = NULL;
12712   char *s_ptr = s;
12713   int pattern_1_len = 0;
12714   int result = ANIM_EVENT_NONE;
12715   int i;
12716
12717   result = get_anim_parameter_value_ce(s);
12718
12719   if (result != ANIM_EVENT_NONE)
12720     return result;
12721
12722   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12723   {
12724     matching_char = strstr(s_ptr, pattern_1[i]);
12725     pattern_1_len = strlen(pattern_1[i]);
12726     result = event_value[i];
12727
12728     if (matching_char != NULL)
12729       break;
12730   }
12731
12732   if (matching_char == NULL)
12733     return ANIM_EVENT_NONE;
12734
12735   s_ptr = matching_char + pattern_1_len;
12736
12737   // check for main animation number ("anim_X" or "anim_XX")
12738   if (*s_ptr >= '0' && *s_ptr <= '9')
12739   {
12740     int gic_anim_nr = (*s_ptr++ - '0');
12741
12742     if (*s_ptr >= '0' && *s_ptr <= '9')
12743       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12744
12745     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12746       return ANIM_EVENT_NONE;
12747
12748     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12749   }
12750   else
12751   {
12752     // invalid main animation number specified
12753
12754     return ANIM_EVENT_NONE;
12755   }
12756
12757   // check for animation part number ("part_X" or "part_XX") (optional)
12758   if (strPrefix(s_ptr, pattern_2))
12759   {
12760     s_ptr += strlen(pattern_2);
12761
12762     if (*s_ptr >= '0' && *s_ptr <= '9')
12763     {
12764       int gic_part_nr = (*s_ptr++ - '0');
12765
12766       if (*s_ptr >= '0' && *s_ptr <= '9')
12767         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12768
12769       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12770         return ANIM_EVENT_NONE;
12771
12772       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12773     }
12774     else
12775     {
12776       // invalid animation part number specified
12777
12778       return ANIM_EVENT_NONE;
12779     }
12780   }
12781
12782   // discard result if next character is neither delimiter nor whitespace
12783   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12784         *s_ptr == ' ' || *s_ptr == '\t'))
12785     return ANIM_EVENT_NONE;
12786
12787   return result;
12788 }
12789
12790 static int get_anim_parameter_values(char *s)
12791 {
12792   int list_pos = ANIM_EVENT_UNDEFINED;
12793   int event_value = ANIM_EVENT_DEFAULT;
12794
12795   if (string_has_parameter(s, "any"))
12796     event_value |= ANIM_EVENT_ANY;
12797
12798   if (string_has_parameter(s, "click:self") ||
12799       string_has_parameter(s, "click") ||
12800       string_has_parameter(s, "self"))
12801     event_value |= ANIM_EVENT_SELF;
12802
12803   if (string_has_parameter(s, "unclick:any"))
12804     event_value |= ANIM_EVENT_UNCLICK_ANY;
12805
12806   // if animation event found, add it to global animation event list
12807   if (event_value != ANIM_EVENT_NONE)
12808     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12809
12810   while (s != NULL)
12811   {
12812     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12813     event_value = get_anim_parameter_value(s);
12814
12815     // if animation event found, add it to global animation event list
12816     if (event_value != ANIM_EVENT_NONE)
12817       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12818
12819     // continue with next part of the string, starting with next comma
12820     s = strchr(s + 1, ',');
12821   }
12822
12823   return list_pos;
12824 }
12825
12826 static int get_anim_action_parameter_value(char *token)
12827 {
12828   // check most common default case first to massively speed things up
12829   if (strEqual(token, ARG_UNDEFINED))
12830     return ANIM_EVENT_ACTION_NONE;
12831
12832   int result = getImageIDFromToken(token);
12833
12834   if (result == -1)
12835   {
12836     char *gfx_token = getStringCat2("gfx.", token);
12837
12838     result = getImageIDFromToken(gfx_token);
12839
12840     checked_free(gfx_token);
12841   }
12842
12843   if (result == -1)
12844   {
12845     Key key = getKeyFromX11KeyName(token);
12846
12847     if (key != KSYM_UNDEFINED)
12848       result = -(int)key;
12849   }
12850
12851   if (result == -1)
12852   {
12853     if (isURL(token))
12854     {
12855       result = get_hash_from_string(token);     // unsigned int => int
12856       result = ABS(result);                     // may be negative now
12857       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12858
12859       setHashEntry(anim_url_hash, int2str(result, 0), token);
12860     }
12861   }
12862
12863   if (result == -1)
12864     result = ANIM_EVENT_ACTION_NONE;
12865
12866   return result;
12867 }
12868
12869 int get_parameter_value(char *value_raw, char *suffix, int type)
12870 {
12871   char *value = getStringToLower(value_raw);
12872   int result = 0;       // probably a save default value
12873
12874   if (strEqual(suffix, ".direction"))
12875   {
12876     result = (strEqual(value, "left")  ? MV_LEFT :
12877               strEqual(value, "right") ? MV_RIGHT :
12878               strEqual(value, "up")    ? MV_UP :
12879               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12880   }
12881   else if (strEqual(suffix, ".position"))
12882   {
12883     result = (strEqual(value, "left")   ? POS_LEFT :
12884               strEqual(value, "right")  ? POS_RIGHT :
12885               strEqual(value, "top")    ? POS_TOP :
12886               strEqual(value, "upper")  ? POS_UPPER :
12887               strEqual(value, "middle") ? POS_MIDDLE :
12888               strEqual(value, "lower")  ? POS_LOWER :
12889               strEqual(value, "bottom") ? POS_BOTTOM :
12890               strEqual(value, "any")    ? POS_ANY :
12891               strEqual(value, "ce")     ? POS_CE :
12892               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12893               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12894   }
12895   else if (strEqual(suffix, ".align"))
12896   {
12897     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12898               strEqual(value, "right")  ? ALIGN_RIGHT :
12899               strEqual(value, "center") ? ALIGN_CENTER :
12900               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12901   }
12902   else if (strEqual(suffix, ".valign"))
12903   {
12904     result = (strEqual(value, "top")    ? VALIGN_TOP :
12905               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12906               strEqual(value, "middle") ? VALIGN_MIDDLE :
12907               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12908   }
12909   else if (strEqual(suffix, ".anim_mode"))
12910   {
12911     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12912               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12913               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12914               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12915               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12916               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12917               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12918               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12919               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12920               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12921               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12922               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12923               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12924               string_has_parameter(value, "all")        ? ANIM_ALL :
12925               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12926               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12927               ANIM_DEFAULT);
12928
12929     if (string_has_parameter(value, "once"))
12930       result |= ANIM_ONCE;
12931
12932     if (string_has_parameter(value, "reverse"))
12933       result |= ANIM_REVERSE;
12934
12935     if (string_has_parameter(value, "opaque_player"))
12936       result |= ANIM_OPAQUE_PLAYER;
12937
12938     if (string_has_parameter(value, "static_panel"))
12939       result |= ANIM_STATIC_PANEL;
12940   }
12941   else if (strEqual(suffix, ".init_event") ||
12942            strEqual(suffix, ".anim_event"))
12943   {
12944     result = get_anim_parameter_values(value);
12945   }
12946   else if (strEqual(suffix, ".init_delay_action") ||
12947            strEqual(suffix, ".anim_delay_action") ||
12948            strEqual(suffix, ".post_delay_action") ||
12949            strEqual(suffix, ".init_event_action") ||
12950            strEqual(suffix, ".anim_event_action"))
12951   {
12952     result = get_anim_action_parameter_value(value_raw);
12953   }
12954   else if (strEqual(suffix, ".class"))
12955   {
12956     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12957               get_hash_from_string(value));
12958   }
12959   else if (strEqual(suffix, ".style"))
12960   {
12961     result = STYLE_DEFAULT;
12962
12963     if (string_has_parameter(value, "accurate_borders"))
12964       result |= STYLE_ACCURATE_BORDERS;
12965
12966     if (string_has_parameter(value, "inner_corners"))
12967       result |= STYLE_INNER_CORNERS;
12968
12969     if (string_has_parameter(value, "reverse"))
12970       result |= STYLE_REVERSE;
12971
12972     if (string_has_parameter(value, "leftmost_position"))
12973       result |= STYLE_LEFTMOST_POSITION;
12974
12975     if (string_has_parameter(value, "block_clicks"))
12976       result |= STYLE_BLOCK;
12977
12978     if (string_has_parameter(value, "passthrough_clicks"))
12979       result |= STYLE_PASSTHROUGH;
12980
12981     if (string_has_parameter(value, "multiple_actions"))
12982       result |= STYLE_MULTIPLE_ACTIONS;
12983
12984     if (string_has_parameter(value, "consume_ce_event"))
12985       result |= STYLE_CONSUME_CE_EVENT;
12986   }
12987   else if (strEqual(suffix, ".fade_mode"))
12988   {
12989     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12990               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12991               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12992               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12993               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12994               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12995               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12996               FADE_MODE_DEFAULT);
12997   }
12998   else if (strEqual(suffix, ".auto_delay_unit"))
12999   {
13000     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13001               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13002               AUTO_DELAY_UNIT_DEFAULT);
13003   }
13004   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13005   {
13006     result = gfx.get_font_from_token_function(value);
13007   }
13008   else          // generic parameter of type integer or boolean
13009   {
13010     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13011               type == TYPE_INTEGER ? get_integer_from_string(value) :
13012               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13013               ARG_UNDEFINED_VALUE);
13014   }
13015
13016   free(value);
13017
13018   return result;
13019 }
13020
13021 static int get_token_parameter_value(char *token, char *value_raw)
13022 {
13023   char *suffix;
13024
13025   if (token == NULL || value_raw == NULL)
13026     return ARG_UNDEFINED_VALUE;
13027
13028   suffix = strrchr(token, '.');
13029   if (suffix == NULL)
13030     suffix = token;
13031
13032   if (strEqual(suffix, ".element"))
13033     return getElementFromToken(value_raw);
13034
13035   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13036   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13037 }
13038
13039 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13040                                      boolean ignore_defaults)
13041 {
13042   int i;
13043
13044   for (i = 0; image_config_vars[i].token != NULL; i++)
13045   {
13046     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13047
13048     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13049     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13050       continue;
13051
13052     if (value != NULL)
13053       *image_config_vars[i].value =
13054         get_token_parameter_value(image_config_vars[i].token, value);
13055   }
13056 }
13057
13058 void InitMenuDesignSettings_Static(void)
13059 {
13060   // always start with reliable default values from static default config
13061   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13062 }
13063
13064 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13065 {
13066   int i;
13067
13068   // the following initializes hierarchical values from static configuration
13069
13070   // special case: initialize "ARG_DEFAULT" values in static default config
13071   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13072   titlescreen_initial_first_default.fade_mode  =
13073     title_initial_first_default.fade_mode;
13074   titlescreen_initial_first_default.fade_delay =
13075     title_initial_first_default.fade_delay;
13076   titlescreen_initial_first_default.post_delay =
13077     title_initial_first_default.post_delay;
13078   titlescreen_initial_first_default.auto_delay =
13079     title_initial_first_default.auto_delay;
13080   titlescreen_initial_first_default.auto_delay_unit =
13081     title_initial_first_default.auto_delay_unit;
13082   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13083   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13084   titlescreen_first_default.post_delay = title_first_default.post_delay;
13085   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13086   titlescreen_first_default.auto_delay_unit =
13087     title_first_default.auto_delay_unit;
13088   titlemessage_initial_first_default.fade_mode  =
13089     title_initial_first_default.fade_mode;
13090   titlemessage_initial_first_default.fade_delay =
13091     title_initial_first_default.fade_delay;
13092   titlemessage_initial_first_default.post_delay =
13093     title_initial_first_default.post_delay;
13094   titlemessage_initial_first_default.auto_delay =
13095     title_initial_first_default.auto_delay;
13096   titlemessage_initial_first_default.auto_delay_unit =
13097     title_initial_first_default.auto_delay_unit;
13098   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13099   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13100   titlemessage_first_default.post_delay = title_first_default.post_delay;
13101   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13102   titlemessage_first_default.auto_delay_unit =
13103     title_first_default.auto_delay_unit;
13104
13105   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13106   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13107   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13108   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13109   titlescreen_initial_default.auto_delay_unit =
13110     title_initial_default.auto_delay_unit;
13111   titlescreen_default.fade_mode  = title_default.fade_mode;
13112   titlescreen_default.fade_delay = title_default.fade_delay;
13113   titlescreen_default.post_delay = title_default.post_delay;
13114   titlescreen_default.auto_delay = title_default.auto_delay;
13115   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13116   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13117   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13118   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13119   titlemessage_initial_default.auto_delay_unit =
13120     title_initial_default.auto_delay_unit;
13121   titlemessage_default.fade_mode  = title_default.fade_mode;
13122   titlemessage_default.fade_delay = title_default.fade_delay;
13123   titlemessage_default.post_delay = title_default.post_delay;
13124   titlemessage_default.auto_delay = title_default.auto_delay;
13125   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13126
13127   // special case: initialize "ARG_DEFAULT" values in static default config
13128   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13129   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13130   {
13131     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13132     titlescreen_first[i] = titlescreen_first_default;
13133     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13134     titlemessage_first[i] = titlemessage_first_default;
13135
13136     titlescreen_initial[i] = titlescreen_initial_default;
13137     titlescreen[i] = titlescreen_default;
13138     titlemessage_initial[i] = titlemessage_initial_default;
13139     titlemessage[i] = titlemessage_default;
13140   }
13141
13142   // special case: initialize "ARG_DEFAULT" values in static default config
13143   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13144   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13145   {
13146     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13147       continue;
13148
13149     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13150     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13151     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13152   }
13153
13154   // special case: initialize "ARG_DEFAULT" values in static default config
13155   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13156   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13157   {
13158     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13159     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13160     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13161
13162     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13163       continue;
13164
13165     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13166   }
13167 }
13168
13169 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13170 {
13171   static struct
13172   {
13173     struct XY *dst, *src;
13174   }
13175   game_buttons_xy[] =
13176   {
13177     { &game.button.save,        &game.button.stop       },
13178     { &game.button.pause2,      &game.button.pause      },
13179     { &game.button.load,        &game.button.play       },
13180     { &game.button.undo,        &game.button.stop       },
13181     { &game.button.redo,        &game.button.play       },
13182
13183     { NULL,                     NULL                    }
13184   };
13185   int i, j;
13186
13187   // special case: initialize later added SETUP list size from LEVELS value
13188   if (menu.list_size[GAME_MODE_SETUP] == -1)
13189     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13190
13191   // set default position for snapshot buttons to stop/pause/play buttons
13192   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13193     if ((*game_buttons_xy[i].dst).x == -1 &&
13194         (*game_buttons_xy[i].dst).y == -1)
13195       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13196
13197   // --------------------------------------------------------------------------
13198   // dynamic viewports (including playfield margins, borders and alignments)
13199   // --------------------------------------------------------------------------
13200
13201   // dynamic viewports currently only supported for landscape mode
13202   int display_width  = MAX(video.display_width, video.display_height);
13203   int display_height = MIN(video.display_width, video.display_height);
13204
13205   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13206   {
13207     struct RectWithBorder *vp_window    = &viewport.window[i];
13208     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13209     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13210     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13211     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13212     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13213     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13214     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13215
13216     // adjust window size if min/max width/height is specified
13217
13218     if (vp_window->min_width != -1)
13219     {
13220       int window_width = display_width;
13221
13222       // when using static window height, use aspect ratio of display
13223       if (vp_window->min_height == -1)
13224         window_width = vp_window->height * display_width / display_height;
13225
13226       vp_window->width = MAX(vp_window->min_width, window_width);
13227     }
13228
13229     if (vp_window->min_height != -1)
13230     {
13231       int window_height = display_height;
13232
13233       // when using static window width, use aspect ratio of display
13234       if (vp_window->min_width == -1)
13235         window_height = vp_window->width * display_height / display_width;
13236
13237       vp_window->height = MAX(vp_window->min_height, window_height);
13238     }
13239
13240     if (vp_window->max_width != -1)
13241       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13242
13243     if (vp_window->max_height != -1)
13244       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13245
13246     int playfield_width  = vp_window->width;
13247     int playfield_height = vp_window->height;
13248
13249     // adjust playfield size and position according to specified margins
13250
13251     playfield_width  -= vp_playfield->margin_left;
13252     playfield_width  -= vp_playfield->margin_right;
13253
13254     playfield_height -= vp_playfield->margin_top;
13255     playfield_height -= vp_playfield->margin_bottom;
13256
13257     // adjust playfield size if min/max width/height is specified
13258
13259     if (vp_playfield->min_width != -1)
13260       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13261
13262     if (vp_playfield->min_height != -1)
13263       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13264
13265     if (vp_playfield->max_width != -1)
13266       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13267
13268     if (vp_playfield->max_height != -1)
13269       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13270
13271     // adjust playfield position according to specified alignment
13272
13273     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13274       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13275     else if (vp_playfield->align == ALIGN_CENTER)
13276       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13277     else if (vp_playfield->align == ALIGN_RIGHT)
13278       vp_playfield->x += playfield_width - vp_playfield->width;
13279
13280     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13281       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13282     else if (vp_playfield->valign == VALIGN_MIDDLE)
13283       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13284     else if (vp_playfield->valign == VALIGN_BOTTOM)
13285       vp_playfield->y += playfield_height - vp_playfield->height;
13286
13287     vp_playfield->x += vp_playfield->margin_left;
13288     vp_playfield->y += vp_playfield->margin_top;
13289
13290     // adjust individual playfield borders if only default border is specified
13291
13292     if (vp_playfield->border_left == -1)
13293       vp_playfield->border_left = vp_playfield->border_size;
13294     if (vp_playfield->border_right == -1)
13295       vp_playfield->border_right = vp_playfield->border_size;
13296     if (vp_playfield->border_top == -1)
13297       vp_playfield->border_top = vp_playfield->border_size;
13298     if (vp_playfield->border_bottom == -1)
13299       vp_playfield->border_bottom = vp_playfield->border_size;
13300
13301     // set dynamic playfield borders if borders are specified as undefined
13302     // (but only if window size was dynamic and playfield size was static)
13303
13304     if (dynamic_window_width && !dynamic_playfield_width)
13305     {
13306       if (vp_playfield->border_left == -1)
13307       {
13308         vp_playfield->border_left = (vp_playfield->x -
13309                                      vp_playfield->margin_left);
13310         vp_playfield->x     -= vp_playfield->border_left;
13311         vp_playfield->width += vp_playfield->border_left;
13312       }
13313
13314       if (vp_playfield->border_right == -1)
13315       {
13316         vp_playfield->border_right = (vp_window->width -
13317                                       vp_playfield->x -
13318                                       vp_playfield->width -
13319                                       vp_playfield->margin_right);
13320         vp_playfield->width += vp_playfield->border_right;
13321       }
13322     }
13323
13324     if (dynamic_window_height && !dynamic_playfield_height)
13325     {
13326       if (vp_playfield->border_top == -1)
13327       {
13328         vp_playfield->border_top = (vp_playfield->y -
13329                                     vp_playfield->margin_top);
13330         vp_playfield->y      -= vp_playfield->border_top;
13331         vp_playfield->height += vp_playfield->border_top;
13332       }
13333
13334       if (vp_playfield->border_bottom == -1)
13335       {
13336         vp_playfield->border_bottom = (vp_window->height -
13337                                        vp_playfield->y -
13338                                        vp_playfield->height -
13339                                        vp_playfield->margin_bottom);
13340         vp_playfield->height += vp_playfield->border_bottom;
13341       }
13342     }
13343
13344     // adjust playfield size to be a multiple of a defined alignment tile size
13345
13346     int align_size = vp_playfield->align_size;
13347     int playfield_xtiles = vp_playfield->width  / align_size;
13348     int playfield_ytiles = vp_playfield->height / align_size;
13349     int playfield_width_corrected  = playfield_xtiles * align_size;
13350     int playfield_height_corrected = playfield_ytiles * align_size;
13351     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13352                                  i == GFX_SPECIAL_ARG_EDITOR);
13353
13354     if (is_playfield_mode &&
13355         dynamic_playfield_width &&
13356         vp_playfield->width != playfield_width_corrected)
13357     {
13358       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13359
13360       vp_playfield->width = playfield_width_corrected;
13361
13362       if (vp_playfield->align == ALIGN_LEFT)
13363       {
13364         vp_playfield->border_left += playfield_xdiff;
13365       }
13366       else if (vp_playfield->align == ALIGN_RIGHT)
13367       {
13368         vp_playfield->border_right += playfield_xdiff;
13369       }
13370       else if (vp_playfield->align == ALIGN_CENTER)
13371       {
13372         int border_left_diff  = playfield_xdiff / 2;
13373         int border_right_diff = playfield_xdiff - border_left_diff;
13374
13375         vp_playfield->border_left  += border_left_diff;
13376         vp_playfield->border_right += border_right_diff;
13377       }
13378     }
13379
13380     if (is_playfield_mode &&
13381         dynamic_playfield_height &&
13382         vp_playfield->height != playfield_height_corrected)
13383     {
13384       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13385
13386       vp_playfield->height = playfield_height_corrected;
13387
13388       if (vp_playfield->valign == VALIGN_TOP)
13389       {
13390         vp_playfield->border_top += playfield_ydiff;
13391       }
13392       else if (vp_playfield->align == VALIGN_BOTTOM)
13393       {
13394         vp_playfield->border_right += playfield_ydiff;
13395       }
13396       else if (vp_playfield->align == VALIGN_MIDDLE)
13397       {
13398         int border_top_diff    = playfield_ydiff / 2;
13399         int border_bottom_diff = playfield_ydiff - border_top_diff;
13400
13401         vp_playfield->border_top    += border_top_diff;
13402         vp_playfield->border_bottom += border_bottom_diff;
13403       }
13404     }
13405
13406     // adjust door positions according to specified alignment
13407
13408     for (j = 0; j < 2; j++)
13409     {
13410       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13411
13412       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13413         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13414       else if (vp_door->align == ALIGN_CENTER)
13415         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13416       else if (vp_door->align == ALIGN_RIGHT)
13417         vp_door->x += vp_window->width - vp_door->width;
13418
13419       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13420         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13421       else if (vp_door->valign == VALIGN_MIDDLE)
13422         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13423       else if (vp_door->valign == VALIGN_BOTTOM)
13424         vp_door->y += vp_window->height - vp_door->height;
13425     }
13426   }
13427 }
13428
13429 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13430 {
13431   static struct
13432   {
13433     struct XYTileSize *dst, *src;
13434     int graphic;
13435   }
13436   editor_buttons_xy[] =
13437   {
13438     {
13439       &editor.button.element_left,      &editor.palette.element_left,
13440       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13441     },
13442     {
13443       &editor.button.element_middle,    &editor.palette.element_middle,
13444       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13445     },
13446     {
13447       &editor.button.element_right,     &editor.palette.element_right,
13448       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13449     },
13450
13451     { NULL,                     NULL                    }
13452   };
13453   int i;
13454
13455   // set default position for element buttons to element graphics
13456   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13457   {
13458     if ((*editor_buttons_xy[i].dst).x == -1 &&
13459         (*editor_buttons_xy[i].dst).y == -1)
13460     {
13461       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13462
13463       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13464
13465       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13466     }
13467   }
13468
13469   // adjust editor palette rows and columns if specified to be dynamic
13470
13471   if (editor.palette.cols == -1)
13472   {
13473     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13474     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13475     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13476
13477     editor.palette.cols = (vp_width - sc_width) / bt_width;
13478
13479     if (editor.palette.x == -1)
13480     {
13481       int palette_width = editor.palette.cols * bt_width + sc_width;
13482
13483       editor.palette.x = (vp_width - palette_width) / 2;
13484     }
13485   }
13486
13487   if (editor.palette.rows == -1)
13488   {
13489     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13490     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13491     int tx_height = getFontHeight(FONT_TEXT_2);
13492
13493     editor.palette.rows = (vp_height - tx_height) / bt_height;
13494
13495     if (editor.palette.y == -1)
13496     {
13497       int palette_height = editor.palette.rows * bt_height + tx_height;
13498
13499       editor.palette.y = (vp_height - palette_height) / 2;
13500     }
13501   }
13502 }
13503
13504 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13505                                                       boolean initialize)
13506 {
13507   // special case: check if network and preview player positions are redefined,
13508   // to compare this later against the main menu level preview being redefined
13509   struct TokenIntPtrInfo menu_config_players[] =
13510   {
13511     { "main.network_players.x", &menu.main.network_players.redefined    },
13512     { "main.network_players.y", &menu.main.network_players.redefined    },
13513     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13514     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13515     { "preview.x",              &preview.redefined                      },
13516     { "preview.y",              &preview.redefined                      }
13517   };
13518   int i;
13519
13520   if (initialize)
13521   {
13522     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13523       *menu_config_players[i].value = FALSE;
13524   }
13525   else
13526   {
13527     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13528       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13529         *menu_config_players[i].value = TRUE;
13530   }
13531 }
13532
13533 static void InitMenuDesignSettings_PreviewPlayers(void)
13534 {
13535   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13536 }
13537
13538 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13539 {
13540   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13541 }
13542
13543 static void LoadMenuDesignSettingsFromFilename(char *filename)
13544 {
13545   static struct TitleFadingInfo tfi;
13546   static struct TitleMessageInfo tmi;
13547   static struct TokenInfo title_tokens[] =
13548   {
13549     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13550     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13551     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13552     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13553     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13554
13555     { -1,               NULL,                   NULL                    }
13556   };
13557   static struct TokenInfo titlemessage_tokens[] =
13558   {
13559     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13560     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13561     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13562     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13563     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13564     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13565     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13566     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13567     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13568     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13569     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13570     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13571     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13572     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13573     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13574     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13575     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13576     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13577
13578     { -1,               NULL,                   NULL                    }
13579   };
13580   static struct
13581   {
13582     struct TitleFadingInfo *info;
13583     char *text;
13584   }
13585   title_info[] =
13586   {
13587     // initialize first titles from "enter screen" definitions, if defined
13588     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13589     { &title_first_default,             "menu.enter_screen.TITLE"       },
13590
13591     // initialize title screens from "next screen" definitions, if defined
13592     { &title_initial_default,           "menu.next_screen.TITLE"        },
13593     { &title_default,                   "menu.next_screen.TITLE"        },
13594
13595     { NULL,                             NULL                            }
13596   };
13597   static struct
13598   {
13599     struct TitleMessageInfo *array;
13600     char *text;
13601   }
13602   titlemessage_arrays[] =
13603   {
13604     // initialize first titles from "enter screen" definitions, if defined
13605     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13606     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13607     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13608     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13609
13610     // initialize titles from "next screen" definitions, if defined
13611     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13612     { titlescreen,                      "menu.next_screen.TITLE"        },
13613     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13614     { titlemessage,                     "menu.next_screen.TITLE"        },
13615
13616     // overwrite titles with title definitions, if defined
13617     { titlescreen_initial_first,        "[title_initial]"               },
13618     { titlescreen_first,                "[title]"                       },
13619     { titlemessage_initial_first,       "[title_initial]"               },
13620     { titlemessage_first,               "[title]"                       },
13621
13622     { titlescreen_initial,              "[title_initial]"               },
13623     { titlescreen,                      "[title]"                       },
13624     { titlemessage_initial,             "[title_initial]"               },
13625     { titlemessage,                     "[title]"                       },
13626
13627     // overwrite titles with title screen/message definitions, if defined
13628     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13629     { titlescreen_first,                "[titlescreen]"                 },
13630     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13631     { titlemessage_first,               "[titlemessage]"                },
13632
13633     { titlescreen_initial,              "[titlescreen_initial]"         },
13634     { titlescreen,                      "[titlescreen]"                 },
13635     { titlemessage_initial,             "[titlemessage_initial]"        },
13636     { titlemessage,                     "[titlemessage]"                },
13637
13638     { NULL,                             NULL                            }
13639   };
13640   SetupFileHash *setup_file_hash;
13641   int i, j, k;
13642
13643   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13644     return;
13645
13646   // the following initializes hierarchical values from dynamic configuration
13647
13648   // special case: initialize with default values that may be overwritten
13649   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13650   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13651   {
13652     struct TokenIntPtrInfo menu_config[] =
13653     {
13654       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13655       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13656       { "menu.list_size",       &menu.list_size[i]      }
13657     };
13658
13659     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13660     {
13661       char *token = menu_config[j].token;
13662       char *value = getHashEntry(setup_file_hash, token);
13663
13664       if (value != NULL)
13665         *menu_config[j].value = get_integer_from_string(value);
13666     }
13667   }
13668
13669   // special case: initialize with default values that may be overwritten
13670   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13671   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13672   {
13673     struct TokenIntPtrInfo menu_config[] =
13674     {
13675       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13676       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13677       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13678       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13679       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13680     };
13681
13682     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13683     {
13684       char *token = menu_config[j].token;
13685       char *value = getHashEntry(setup_file_hash, token);
13686
13687       if (value != NULL)
13688         *menu_config[j].value = get_integer_from_string(value);
13689     }
13690   }
13691
13692   // special case: initialize with default values that may be overwritten
13693   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13694   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13695   {
13696     struct TokenIntPtrInfo menu_config[] =
13697     {
13698       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13699       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13700     };
13701
13702     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13703     {
13704       char *token = menu_config[j].token;
13705       char *value = getHashEntry(setup_file_hash, token);
13706
13707       if (value != NULL)
13708         *menu_config[j].value = get_integer_from_string(value);
13709     }
13710   }
13711
13712   // special case: initialize with default values that may be overwritten
13713   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13714   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13715   {
13716     struct TokenIntPtrInfo menu_config[] =
13717     {
13718       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13719       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13720       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13721       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13722       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13723       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13724       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13725       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13726       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13727       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
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_integer_from_string(value);
13737     }
13738   }
13739
13740   // special case: initialize with default values that may be overwritten
13741   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13742   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13743   {
13744     struct TokenIntPtrInfo menu_config[] =
13745     {
13746       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13747       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13748       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13749       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13750       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13751       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13752       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13753       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13754       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13755     };
13756
13757     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13758     {
13759       char *token = menu_config[j].token;
13760       char *value = getHashEntry(setup_file_hash, token);
13761
13762       if (value != NULL)
13763         *menu_config[j].value = get_token_parameter_value(token, value);
13764     }
13765   }
13766
13767   // special case: initialize with default values that may be overwritten
13768   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13769   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13770   {
13771     struct
13772     {
13773       char *token_prefix;
13774       struct RectWithBorder *struct_ptr;
13775     }
13776     vp_struct[] =
13777     {
13778       { "viewport.window",      &viewport.window[i]     },
13779       { "viewport.playfield",   &viewport.playfield[i]  },
13780       { "viewport.door_1",      &viewport.door_1[i]     },
13781       { "viewport.door_2",      &viewport.door_2[i]     }
13782     };
13783
13784     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13785     {
13786       struct TokenIntPtrInfo vp_config[] =
13787       {
13788         { ".x",                 &vp_struct[j].struct_ptr->x             },
13789         { ".y",                 &vp_struct[j].struct_ptr->y             },
13790         { ".width",             &vp_struct[j].struct_ptr->width         },
13791         { ".height",            &vp_struct[j].struct_ptr->height        },
13792         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13793         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13794         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13795         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13796         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13797         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13798         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13799         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13800         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13801         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13802         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13803         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13804         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13805         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13806         { ".align",             &vp_struct[j].struct_ptr->align         },
13807         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13808       };
13809
13810       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13811       {
13812         char *token = getStringCat2(vp_struct[j].token_prefix,
13813                                     vp_config[k].token);
13814         char *value = getHashEntry(setup_file_hash, token);
13815
13816         if (value != NULL)
13817           *vp_config[k].value = get_token_parameter_value(token, value);
13818
13819         free(token);
13820       }
13821     }
13822   }
13823
13824   // special case: initialize with default values that may be overwritten
13825   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13826   for (i = 0; title_info[i].info != NULL; i++)
13827   {
13828     struct TitleFadingInfo *info = title_info[i].info;
13829     char *base_token = title_info[i].text;
13830
13831     for (j = 0; title_tokens[j].type != -1; j++)
13832     {
13833       char *token = getStringCat2(base_token, title_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         tfi = *info;
13841
13842         *(int *)title_tokens[j].value = (int)parameter_value;
13843
13844         *info = tfi;
13845       }
13846
13847       free(token);
13848     }
13849   }
13850
13851   // special case: initialize with default values that may be overwritten
13852   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13853   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13854   {
13855     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13856     char *base_token = titlemessage_arrays[i].text;
13857
13858     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13859     {
13860       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13861       char *value = getHashEntry(setup_file_hash, token);
13862
13863       if (value != NULL)
13864       {
13865         int parameter_value = get_token_parameter_value(token, value);
13866
13867         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13868         {
13869           tmi = array[k];
13870
13871           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13872             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13873           else
13874             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13875
13876           array[k] = tmi;
13877         }
13878       }
13879
13880       free(token);
13881     }
13882   }
13883
13884   // read (and overwrite with) values that may be specified in config file
13885   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13886
13887   // special case: check if network and preview player positions are redefined
13888   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13889
13890   freeSetupFileHash(setup_file_hash);
13891 }
13892
13893 void LoadMenuDesignSettings(void)
13894 {
13895   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13896
13897   InitMenuDesignSettings_Static();
13898   InitMenuDesignSettings_SpecialPreProcessing();
13899   InitMenuDesignSettings_PreviewPlayers();
13900
13901   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13902   {
13903     // first look for special settings configured in level series config
13904     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13905
13906     if (fileExists(filename_base))
13907       LoadMenuDesignSettingsFromFilename(filename_base);
13908   }
13909
13910   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13911
13912   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13913     LoadMenuDesignSettingsFromFilename(filename_local);
13914
13915   InitMenuDesignSettings_SpecialPostProcessing();
13916 }
13917
13918 void LoadMenuDesignSettings_AfterGraphics(void)
13919 {
13920   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13921 }
13922
13923 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13924                                 boolean ignore_defaults)
13925 {
13926   int i;
13927
13928   for (i = 0; sound_config_vars[i].token != NULL; i++)
13929   {
13930     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13931
13932     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13933     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13934       continue;
13935
13936     if (value != NULL)
13937       *sound_config_vars[i].value =
13938         get_token_parameter_value(sound_config_vars[i].token, value);
13939   }
13940 }
13941
13942 void InitSoundSettings_Static(void)
13943 {
13944   // always start with reliable default values from static default config
13945   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13946 }
13947
13948 static void LoadSoundSettingsFromFilename(char *filename)
13949 {
13950   SetupFileHash *setup_file_hash;
13951
13952   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13953     return;
13954
13955   // read (and overwrite with) values that may be specified in config file
13956   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13957
13958   freeSetupFileHash(setup_file_hash);
13959 }
13960
13961 void LoadSoundSettings(void)
13962 {
13963   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13964
13965   InitSoundSettings_Static();
13966
13967   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13968   {
13969     // first look for special settings configured in level series config
13970     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13971
13972     if (fileExists(filename_base))
13973       LoadSoundSettingsFromFilename(filename_base);
13974   }
13975
13976   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13977
13978   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13979     LoadSoundSettingsFromFilename(filename_local);
13980 }
13981
13982 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13983 {
13984   char *filename = getEditorSetupFilename();
13985   SetupFileList *setup_file_list, *list;
13986   SetupFileHash *element_hash;
13987   int num_unknown_tokens = 0;
13988   int i;
13989
13990   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13991     return;
13992
13993   element_hash = newSetupFileHash();
13994
13995   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13996     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13997
13998   // determined size may be larger than needed (due to unknown elements)
13999   *num_elements = 0;
14000   for (list = setup_file_list; list != NULL; list = list->next)
14001     (*num_elements)++;
14002
14003   // add space for up to 3 more elements for padding that may be needed
14004   *num_elements += 3;
14005
14006   // free memory for old list of elements, if needed
14007   checked_free(*elements);
14008
14009   // allocate memory for new list of elements
14010   *elements = checked_malloc(*num_elements * sizeof(int));
14011
14012   *num_elements = 0;
14013   for (list = setup_file_list; list != NULL; list = list->next)
14014   {
14015     char *value = getHashEntry(element_hash, list->token);
14016
14017     if (value == NULL)          // try to find obsolete token mapping
14018     {
14019       char *mapped_token = get_mapped_token(list->token);
14020
14021       if (mapped_token != NULL)
14022       {
14023         value = getHashEntry(element_hash, mapped_token);
14024
14025         free(mapped_token);
14026       }
14027     }
14028
14029     if (value != NULL)
14030     {
14031       (*elements)[(*num_elements)++] = atoi(value);
14032     }
14033     else
14034     {
14035       if (num_unknown_tokens == 0)
14036       {
14037         Warn("---");
14038         Warn("unknown token(s) found in config file:");
14039         Warn("- config file: '%s'", filename);
14040
14041         num_unknown_tokens++;
14042       }
14043
14044       Warn("- token: '%s'", list->token);
14045     }
14046   }
14047
14048   if (num_unknown_tokens > 0)
14049     Warn("---");
14050
14051   while (*num_elements % 4)     // pad with empty elements, if needed
14052     (*elements)[(*num_elements)++] = EL_EMPTY;
14053
14054   freeSetupFileList(setup_file_list);
14055   freeSetupFileHash(element_hash);
14056
14057 #if 0
14058   for (i = 0; i < *num_elements; i++)
14059     Debug("editor", "element '%s' [%d]\n",
14060           element_info[(*elements)[i]].token_name, (*elements)[i]);
14061 #endif
14062 }
14063
14064 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14065                                                      boolean is_sound)
14066 {
14067   SetupFileHash *setup_file_hash = NULL;
14068   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14069   char *filename_music, *filename_prefix, *filename_info;
14070   struct
14071   {
14072     char *token;
14073     char **value_ptr;
14074   }
14075   token_to_value_ptr[] =
14076   {
14077     { "title_header",   &tmp_music_file_info.title_header       },
14078     { "artist_header",  &tmp_music_file_info.artist_header      },
14079     { "album_header",   &tmp_music_file_info.album_header       },
14080     { "year_header",    &tmp_music_file_info.year_header        },
14081     { "played_header",  &tmp_music_file_info.played_header      },
14082
14083     { "title",          &tmp_music_file_info.title              },
14084     { "artist",         &tmp_music_file_info.artist             },
14085     { "album",          &tmp_music_file_info.album              },
14086     { "year",           &tmp_music_file_info.year               },
14087     { "played",         &tmp_music_file_info.played             },
14088
14089     { NULL,             NULL                                    },
14090   };
14091   int i;
14092
14093   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14094                     getCustomMusicFilename(basename));
14095
14096   if (filename_music == NULL)
14097     return NULL;
14098
14099   // ---------- try to replace file extension ----------
14100
14101   filename_prefix = getStringCopy(filename_music);
14102   if (strrchr(filename_prefix, '.') != NULL)
14103     *strrchr(filename_prefix, '.') = '\0';
14104   filename_info = getStringCat2(filename_prefix, ".txt");
14105
14106   if (fileExists(filename_info))
14107     setup_file_hash = loadSetupFileHash(filename_info);
14108
14109   free(filename_prefix);
14110   free(filename_info);
14111
14112   if (setup_file_hash == NULL)
14113   {
14114     // ---------- try to add file extension ----------
14115
14116     filename_prefix = getStringCopy(filename_music);
14117     filename_info = getStringCat2(filename_prefix, ".txt");
14118
14119     if (fileExists(filename_info))
14120       setup_file_hash = loadSetupFileHash(filename_info);
14121
14122     free(filename_prefix);
14123     free(filename_info);
14124   }
14125
14126   if (setup_file_hash == NULL)
14127     return NULL;
14128
14129   // ---------- music file info found ----------
14130
14131   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14132
14133   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14134   {
14135     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14136
14137     *token_to_value_ptr[i].value_ptr =
14138       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14139   }
14140
14141   tmp_music_file_info.basename = getStringCopy(basename);
14142   tmp_music_file_info.music = music;
14143   tmp_music_file_info.is_sound = is_sound;
14144
14145   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14146   *new_music_file_info = tmp_music_file_info;
14147
14148   return new_music_file_info;
14149 }
14150
14151 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14152 {
14153   return get_music_file_info_ext(basename, music, FALSE);
14154 }
14155
14156 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14157 {
14158   return get_music_file_info_ext(basename, sound, TRUE);
14159 }
14160
14161 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14162                                      char *basename, boolean is_sound)
14163 {
14164   for (; list != NULL; list = list->next)
14165     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14166       return TRUE;
14167
14168   return FALSE;
14169 }
14170
14171 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14172 {
14173   return music_info_listed_ext(list, basename, FALSE);
14174 }
14175
14176 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14177 {
14178   return music_info_listed_ext(list, basename, TRUE);
14179 }
14180
14181 void LoadMusicInfo(void)
14182 {
14183   int num_music_noconf = getMusicListSize_NoConf();
14184   int num_music = getMusicListSize();
14185   int num_sounds = getSoundListSize();
14186   struct FileInfo *music, *sound;
14187   struct MusicFileInfo *next, **new;
14188
14189   int i;
14190
14191   while (music_file_info != NULL)
14192   {
14193     next = music_file_info->next;
14194
14195     checked_free(music_file_info->basename);
14196
14197     checked_free(music_file_info->title_header);
14198     checked_free(music_file_info->artist_header);
14199     checked_free(music_file_info->album_header);
14200     checked_free(music_file_info->year_header);
14201     checked_free(music_file_info->played_header);
14202
14203     checked_free(music_file_info->title);
14204     checked_free(music_file_info->artist);
14205     checked_free(music_file_info->album);
14206     checked_free(music_file_info->year);
14207     checked_free(music_file_info->played);
14208
14209     free(music_file_info);
14210
14211     music_file_info = next;
14212   }
14213
14214   new = &music_file_info;
14215
14216   // get (configured or unconfigured) music file info for all levels
14217   for (i = leveldir_current->first_level;
14218        i <= leveldir_current->last_level; i++)
14219   {
14220     int music_nr;
14221
14222     if (levelset.music[i] != MUS_UNDEFINED)
14223     {
14224       // get music file info for configured level music
14225       music_nr = levelset.music[i];
14226     }
14227     else if (num_music_noconf > 0)
14228     {
14229       // get music file info for unconfigured level music
14230       int level_pos = i - leveldir_current->first_level;
14231
14232       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14233     }
14234     else
14235     {
14236       continue;
14237     }
14238
14239     char *basename = getMusicInfoEntryFilename(music_nr);
14240
14241     if (basename == NULL)
14242       continue;
14243
14244     if (!music_info_listed(music_file_info, basename))
14245     {
14246       *new = get_music_file_info(basename, music_nr);
14247
14248       if (*new != NULL)
14249         new = &(*new)->next;
14250     }
14251   }
14252
14253   // get music file info for all remaining configured music files
14254   for (i = 0; i < num_music; i++)
14255   {
14256     music = getMusicListEntry(i);
14257
14258     if (music->filename == NULL)
14259       continue;
14260
14261     if (strEqual(music->filename, UNDEFINED_FILENAME))
14262       continue;
14263
14264     // a configured file may be not recognized as music
14265     if (!FileIsMusic(music->filename))
14266       continue;
14267
14268     if (!music_info_listed(music_file_info, music->filename))
14269     {
14270       *new = get_music_file_info(music->filename, i);
14271
14272       if (*new != NULL)
14273         new = &(*new)->next;
14274     }
14275   }
14276
14277   // get sound file info for all configured sound files
14278   for (i = 0; i < num_sounds; i++)
14279   {
14280     sound = getSoundListEntry(i);
14281
14282     if (sound->filename == NULL)
14283       continue;
14284
14285     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14286       continue;
14287
14288     // a configured file may be not recognized as sound
14289     if (!FileIsSound(sound->filename))
14290       continue;
14291
14292     if (!sound_info_listed(music_file_info, sound->filename))
14293     {
14294       *new = get_sound_file_info(sound->filename, i);
14295       if (*new != NULL)
14296         new = &(*new)->next;
14297     }
14298   }
14299
14300   // add pointers to previous list nodes
14301
14302   struct MusicFileInfo *node = music_file_info;
14303
14304   while (node != NULL)
14305   {
14306     if (node->next)
14307       node->next->prev = node;
14308
14309     node = node->next;
14310   }
14311 }
14312
14313 static void add_helpanim_entry(int element, int action, int direction,
14314                                int delay, int *num_list_entries)
14315 {
14316   struct HelpAnimInfo *new_list_entry;
14317   (*num_list_entries)++;
14318
14319   helpanim_info =
14320     checked_realloc(helpanim_info,
14321                     *num_list_entries * sizeof(struct HelpAnimInfo));
14322   new_list_entry = &helpanim_info[*num_list_entries - 1];
14323
14324   new_list_entry->element = element;
14325   new_list_entry->action = action;
14326   new_list_entry->direction = direction;
14327   new_list_entry->delay = delay;
14328 }
14329
14330 static void print_unknown_token(char *filename, char *token, int token_nr)
14331 {
14332   if (token_nr == 0)
14333   {
14334     Warn("---");
14335     Warn("unknown token(s) found in config file:");
14336     Warn("- config file: '%s'", filename);
14337   }
14338
14339   Warn("- token: '%s'", token);
14340 }
14341
14342 static void print_unknown_token_end(int token_nr)
14343 {
14344   if (token_nr > 0)
14345     Warn("---");
14346 }
14347
14348 void LoadHelpAnimInfo(void)
14349 {
14350   char *filename = getHelpAnimFilename();
14351   SetupFileList *setup_file_list = NULL, *list;
14352   SetupFileHash *element_hash, *action_hash, *direction_hash;
14353   int num_list_entries = 0;
14354   int num_unknown_tokens = 0;
14355   int i;
14356
14357   if (fileExists(filename))
14358     setup_file_list = loadSetupFileList(filename);
14359
14360   if (setup_file_list == NULL)
14361   {
14362     // use reliable default values from static configuration
14363     SetupFileList *insert_ptr;
14364
14365     insert_ptr = setup_file_list =
14366       newSetupFileList(helpanim_config[0].token,
14367                        helpanim_config[0].value);
14368
14369     for (i = 1; helpanim_config[i].token; i++)
14370       insert_ptr = addListEntry(insert_ptr,
14371                                 helpanim_config[i].token,
14372                                 helpanim_config[i].value);
14373   }
14374
14375   element_hash   = newSetupFileHash();
14376   action_hash    = newSetupFileHash();
14377   direction_hash = newSetupFileHash();
14378
14379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14380     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14381
14382   for (i = 0; i < NUM_ACTIONS; i++)
14383     setHashEntry(action_hash, element_action_info[i].suffix,
14384                  i_to_a(element_action_info[i].value));
14385
14386   // do not store direction index (bit) here, but direction value!
14387   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14388     setHashEntry(direction_hash, element_direction_info[i].suffix,
14389                  i_to_a(1 << element_direction_info[i].value));
14390
14391   for (list = setup_file_list; list != NULL; list = list->next)
14392   {
14393     char *element_token, *action_token, *direction_token;
14394     char *element_value, *action_value, *direction_value;
14395     int delay = atoi(list->value);
14396
14397     if (strEqual(list->token, "end"))
14398     {
14399       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14400
14401       continue;
14402     }
14403
14404     /* first try to break element into element/action/direction parts;
14405        if this does not work, also accept combined "element[.act][.dir]"
14406        elements (like "dynamite.active"), which are unique elements */
14407
14408     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14409     {
14410       element_value = getHashEntry(element_hash, list->token);
14411       if (element_value != NULL)        // element found
14412         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14413                            &num_list_entries);
14414       else
14415       {
14416         // no further suffixes found -- this is not an element
14417         print_unknown_token(filename, list->token, num_unknown_tokens++);
14418       }
14419
14420       continue;
14421     }
14422
14423     // token has format "<prefix>.<something>"
14424
14425     action_token = strchr(list->token, '.');    // suffix may be action ...
14426     direction_token = action_token;             // ... or direction
14427
14428     element_token = getStringCopy(list->token);
14429     *strchr(element_token, '.') = '\0';
14430
14431     element_value = getHashEntry(element_hash, element_token);
14432
14433     if (element_value == NULL)          // this is no element
14434     {
14435       element_value = getHashEntry(element_hash, list->token);
14436       if (element_value != NULL)        // combined element found
14437         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14438                            &num_list_entries);
14439       else
14440         print_unknown_token(filename, list->token, num_unknown_tokens++);
14441
14442       free(element_token);
14443
14444       continue;
14445     }
14446
14447     action_value = getHashEntry(action_hash, action_token);
14448
14449     if (action_value != NULL)           // action found
14450     {
14451       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14452                     &num_list_entries);
14453
14454       free(element_token);
14455
14456       continue;
14457     }
14458
14459     direction_value = getHashEntry(direction_hash, direction_token);
14460
14461     if (direction_value != NULL)        // direction found
14462     {
14463       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14464                          &num_list_entries);
14465
14466       free(element_token);
14467
14468       continue;
14469     }
14470
14471     if (strchr(action_token + 1, '.') == NULL)
14472     {
14473       // no further suffixes found -- this is not an action nor direction
14474
14475       element_value = getHashEntry(element_hash, list->token);
14476       if (element_value != NULL)        // combined element found
14477         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14478                            &num_list_entries);
14479       else
14480         print_unknown_token(filename, list->token, num_unknown_tokens++);
14481
14482       free(element_token);
14483
14484       continue;
14485     }
14486
14487     // token has format "<prefix>.<suffix>.<something>"
14488
14489     direction_token = strchr(action_token + 1, '.');
14490
14491     action_token = getStringCopy(action_token);
14492     *strchr(action_token + 1, '.') = '\0';
14493
14494     action_value = getHashEntry(action_hash, action_token);
14495
14496     if (action_value == NULL)           // this is no action
14497     {
14498       element_value = getHashEntry(element_hash, list->token);
14499       if (element_value != NULL)        // combined element found
14500         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14501                            &num_list_entries);
14502       else
14503         print_unknown_token(filename, list->token, num_unknown_tokens++);
14504
14505       free(element_token);
14506       free(action_token);
14507
14508       continue;
14509     }
14510
14511     direction_value = getHashEntry(direction_hash, direction_token);
14512
14513     if (direction_value != NULL)        // direction found
14514     {
14515       add_helpanim_entry(atoi(element_value), atoi(action_value),
14516                          atoi(direction_value), delay, &num_list_entries);
14517
14518       free(element_token);
14519       free(action_token);
14520
14521       continue;
14522     }
14523
14524     // this is no direction
14525
14526     element_value = getHashEntry(element_hash, list->token);
14527     if (element_value != NULL)          // combined element found
14528       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14529                          &num_list_entries);
14530     else
14531       print_unknown_token(filename, list->token, num_unknown_tokens++);
14532
14533     free(element_token);
14534     free(action_token);
14535   }
14536
14537   print_unknown_token_end(num_unknown_tokens);
14538
14539   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14540   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14541
14542   freeSetupFileList(setup_file_list);
14543   freeSetupFileHash(element_hash);
14544   freeSetupFileHash(action_hash);
14545   freeSetupFileHash(direction_hash);
14546
14547 #if 0
14548   for (i = 0; i < num_list_entries; i++)
14549     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14550           EL_NAME(helpanim_info[i].element),
14551           helpanim_info[i].element,
14552           helpanim_info[i].action,
14553           helpanim_info[i].direction,
14554           helpanim_info[i].delay);
14555 #endif
14556 }
14557
14558 void LoadHelpTextInfo(void)
14559 {
14560   char *filename = getHelpTextFilename();
14561   int i;
14562
14563   if (helptext_info != NULL)
14564   {
14565     freeSetupFileHash(helptext_info);
14566     helptext_info = NULL;
14567   }
14568
14569   if (fileExists(filename))
14570     helptext_info = loadSetupFileHash(filename);
14571
14572   if (helptext_info == NULL)
14573   {
14574     // use reliable default values from static configuration
14575     helptext_info = newSetupFileHash();
14576
14577     for (i = 0; helptext_config[i].token; i++)
14578       setHashEntry(helptext_info,
14579                    helptext_config[i].token,
14580                    helptext_config[i].value);
14581   }
14582
14583 #if 0
14584   BEGIN_HASH_ITERATION(helptext_info, itr)
14585   {
14586     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14587           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14588   }
14589   END_HASH_ITERATION(hash, itr)
14590 #endif
14591 }
14592
14593
14594 // ----------------------------------------------------------------------------
14595 // convert levels
14596 // ----------------------------------------------------------------------------
14597
14598 #define MAX_NUM_CONVERT_LEVELS          1000
14599
14600 void ConvertLevels(void)
14601 {
14602   static LevelDirTree *convert_leveldir = NULL;
14603   static int convert_level_nr = -1;
14604   static int num_levels_handled = 0;
14605   static int num_levels_converted = 0;
14606   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14607   int i;
14608
14609   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14610                                                global.convert_leveldir);
14611
14612   if (convert_leveldir == NULL)
14613     Fail("no such level identifier: '%s'", global.convert_leveldir);
14614
14615   leveldir_current = convert_leveldir;
14616
14617   if (global.convert_level_nr != -1)
14618   {
14619     convert_leveldir->first_level = global.convert_level_nr;
14620     convert_leveldir->last_level  = global.convert_level_nr;
14621   }
14622
14623   convert_level_nr = convert_leveldir->first_level;
14624
14625   PrintLine("=", 79);
14626   Print("Converting levels\n");
14627   PrintLine("-", 79);
14628   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14629   Print("Level series name:       '%s'\n", convert_leveldir->name);
14630   Print("Level series author:     '%s'\n", convert_leveldir->author);
14631   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14632   PrintLine("=", 79);
14633   Print("\n");
14634
14635   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14636     levels_failed[i] = FALSE;
14637
14638   while (convert_level_nr <= convert_leveldir->last_level)
14639   {
14640     char *level_filename;
14641     boolean new_level;
14642
14643     level_nr = convert_level_nr++;
14644
14645     Print("Level %03d: ", level_nr);
14646
14647     LoadLevel(level_nr);
14648     if (level.no_level_file || level.no_valid_file)
14649     {
14650       Print("(no level)\n");
14651       continue;
14652     }
14653
14654     Print("converting level ... ");
14655
14656 #if 0
14657     // special case: conversion of some EMC levels as requested by ACME
14658     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14659 #endif
14660
14661     level_filename = getDefaultLevelFilename(level_nr);
14662     new_level = !fileExists(level_filename);
14663
14664     if (new_level)
14665     {
14666       SaveLevel(level_nr);
14667
14668       num_levels_converted++;
14669
14670       Print("converted.\n");
14671     }
14672     else
14673     {
14674       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14675         levels_failed[level_nr] = TRUE;
14676
14677       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14678     }
14679
14680     num_levels_handled++;
14681   }
14682
14683   Print("\n");
14684   PrintLine("=", 79);
14685   Print("Number of levels handled: %d\n", num_levels_handled);
14686   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14687          (num_levels_handled ?
14688           num_levels_converted * 100 / num_levels_handled : 0));
14689   PrintLine("-", 79);
14690   Print("Summary (for automatic parsing by scripts):\n");
14691   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14692          convert_leveldir->identifier, num_levels_converted,
14693          num_levels_handled,
14694          (num_levels_handled ?
14695           num_levels_converted * 100 / num_levels_handled : 0));
14696
14697   if (num_levels_handled != num_levels_converted)
14698   {
14699     Print(", FAILED:");
14700     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14701       if (levels_failed[i])
14702         Print(" %03d", i);
14703   }
14704
14705   Print("\n");
14706   PrintLine("=", 79);
14707
14708   CloseAllAndExit(0);
14709 }
14710
14711
14712 // ----------------------------------------------------------------------------
14713 // create and save images for use in level sketches (raw BMP format)
14714 // ----------------------------------------------------------------------------
14715
14716 void CreateLevelSketchImages(void)
14717 {
14718   Bitmap *bitmap1;
14719   Bitmap *bitmap2;
14720   int i;
14721
14722   InitElementPropertiesGfxElement();
14723
14724   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14725   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14726
14727   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14728   {
14729     int element = getMappedElement(i);
14730     char basename1[16];
14731     char basename2[16];
14732     char *filename1;
14733     char *filename2;
14734
14735     sprintf(basename1, "%04d.bmp", i);
14736     sprintf(basename2, "%04ds.bmp", i);
14737
14738     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14739     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14740
14741     DrawSizedElement(0, 0, element, TILESIZE);
14742     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14743
14744     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14745       Fail("cannot save level sketch image file '%s'", filename1);
14746
14747     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14748     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14749
14750     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14751       Fail("cannot save level sketch image file '%s'", filename2);
14752
14753     free(filename1);
14754     free(filename2);
14755
14756     // create corresponding SQL statements (for normal and small images)
14757     if (i < 1000)
14758     {
14759       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14760       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14761     }
14762
14763     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14764     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14765
14766     // optional: create content for forum level sketch demonstration post
14767     if (options.debug)
14768       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14769   }
14770
14771   FreeBitmap(bitmap1);
14772   FreeBitmap(bitmap2);
14773
14774   if (options.debug)
14775     fprintf(stderr, "\n");
14776
14777   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14778
14779   CloseAllAndExit(0);
14780 }
14781
14782
14783 // ----------------------------------------------------------------------------
14784 // create and save images for element collecting animations (raw BMP format)
14785 // ----------------------------------------------------------------------------
14786
14787 static boolean createCollectImage(int element)
14788 {
14789   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14790 }
14791
14792 void CreateCollectElementImages(void)
14793 {
14794   int i, j;
14795   int num_steps = 8;
14796   int anim_frames = num_steps - 1;
14797   int tile_size = TILESIZE;
14798   int anim_width  = tile_size * anim_frames;
14799   int anim_height = tile_size;
14800   int num_collect_images = 0;
14801   int pos_collect_images = 0;
14802
14803   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14804     if (createCollectImage(i))
14805       num_collect_images++;
14806
14807   Info("Creating %d element collecting animation images ...",
14808        num_collect_images);
14809
14810   int dst_width  = anim_width * 2;
14811   int dst_height = anim_height * num_collect_images / 2;
14812   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14813   char *basename_bmp = "RocksCollect.bmp";
14814   char *basename_png = "RocksCollect.png";
14815   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14816   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14817   int len_filename_bmp = strlen(filename_bmp);
14818   int len_filename_png = strlen(filename_png);
14819   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14820   char cmd_convert[max_command_len];
14821
14822   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14823            filename_bmp,
14824            filename_png);
14825
14826   // force using RGBA surface for destination bitmap
14827   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14828                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14829
14830   dst_bitmap->surface =
14831     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14832
14833   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14834   {
14835     if (!createCollectImage(i))
14836       continue;
14837
14838     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14839     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14840     int graphic = el2img(i);
14841     char *token_name = element_info[i].token_name;
14842     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14843     Bitmap *src_bitmap;
14844     int src_x, src_y;
14845
14846     Info("- creating collecting image for '%s' ...", token_name);
14847
14848     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14849
14850     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14851                tile_size, tile_size, 0, 0);
14852
14853     // force using RGBA surface for temporary bitmap (using transparent black)
14854     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14855                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14856
14857     tmp_bitmap->surface =
14858       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14859
14860     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14861
14862     for (j = 0; j < anim_frames; j++)
14863     {
14864       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14865       int frame_size = frame_size_final * num_steps;
14866       int offset = (tile_size - frame_size_final) / 2;
14867       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14868
14869       while (frame_size > frame_size_final)
14870       {
14871         frame_size /= 2;
14872
14873         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14874
14875         FreeBitmap(frame_bitmap);
14876
14877         frame_bitmap = half_bitmap;
14878       }
14879
14880       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14881                        frame_size_final, frame_size_final,
14882                        dst_x + j * tile_size + offset, dst_y + offset);
14883
14884       FreeBitmap(frame_bitmap);
14885     }
14886
14887     tmp_bitmap->surface_masked = NULL;
14888
14889     FreeBitmap(tmp_bitmap);
14890
14891     pos_collect_images++;
14892   }
14893
14894   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14895     Fail("cannot save element collecting image file '%s'", filename_bmp);
14896
14897   FreeBitmap(dst_bitmap);
14898
14899   Info("Converting image file from BMP to PNG ...");
14900
14901   if (system(cmd_convert) != 0)
14902     Fail("converting image file failed");
14903
14904   unlink(filename_bmp);
14905
14906   Info("Done.");
14907
14908   CloseAllAndExit(0);
14909 }
14910
14911
14912 // ----------------------------------------------------------------------------
14913 // create and save images for custom and group elements (raw BMP format)
14914 // ----------------------------------------------------------------------------
14915
14916 void CreateCustomElementImages(char *directory)
14917 {
14918   char *src_basename = "RocksCE-template.ilbm";
14919   char *dst_basename = "RocksCE.bmp";
14920   char *src_filename = getPath2(directory, src_basename);
14921   char *dst_filename = getPath2(directory, dst_basename);
14922   Bitmap *src_bitmap;
14923   Bitmap *bitmap;
14924   int yoffset_ce = 0;
14925   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14926   int i;
14927
14928   InitVideoDefaults();
14929
14930   ReCreateBitmap(&backbuffer, video.width, video.height);
14931
14932   src_bitmap = LoadImage(src_filename);
14933
14934   bitmap = CreateBitmap(TILEX * 16 * 2,
14935                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14936                         DEFAULT_DEPTH);
14937
14938   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14939   {
14940     int x = i % 16;
14941     int y = i / 16;
14942     int ii = i + 1;
14943     int j;
14944
14945     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14946                TILEX * x, TILEY * y + yoffset_ce);
14947
14948     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14949                TILEX, TILEY,
14950                TILEX * x + TILEX * 16,
14951                TILEY * y + yoffset_ce);
14952
14953     for (j = 2; j >= 0; j--)
14954     {
14955       int c = ii % 10;
14956
14957       BlitBitmap(src_bitmap, bitmap,
14958                  TILEX + c * 7, 0, 6, 10,
14959                  TILEX * x + 6 + j * 7,
14960                  TILEY * y + 11 + yoffset_ce);
14961
14962       BlitBitmap(src_bitmap, bitmap,
14963                  TILEX + c * 8, TILEY, 6, 10,
14964                  TILEX * 16 + TILEX * x + 6 + j * 8,
14965                  TILEY * y + 10 + yoffset_ce);
14966
14967       ii /= 10;
14968     }
14969   }
14970
14971   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14972   {
14973     int x = i % 16;
14974     int y = i / 16;
14975     int ii = i + 1;
14976     int j;
14977
14978     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14979                TILEX * x, TILEY * y + yoffset_ge);
14980
14981     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14982                TILEX, TILEY,
14983                TILEX * x + TILEX * 16,
14984                TILEY * y + yoffset_ge);
14985
14986     for (j = 1; j >= 0; j--)
14987     {
14988       int c = ii % 10;
14989
14990       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14991                  TILEX * x + 6 + j * 10,
14992                  TILEY * y + 11 + yoffset_ge);
14993
14994       BlitBitmap(src_bitmap, bitmap,
14995                  TILEX + c * 8, TILEY + 12, 6, 10,
14996                  TILEX * 16 + TILEX * x + 10 + j * 8,
14997                  TILEY * y + 10 + yoffset_ge);
14998
14999       ii /= 10;
15000     }
15001   }
15002
15003   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15004     Fail("cannot save CE graphics file '%s'", dst_filename);
15005
15006   FreeBitmap(bitmap);
15007
15008   CloseAllAndExit(0);
15009 }