added option for magic wall timer of zero 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_ELEMENT,                       CONF_VALUE_16_BIT(1),
748     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
749   },
750   {
751     EL_BD_MAGIC_WALL,                   -1,
752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
753     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
754   },
755   {
756     EL_BD_MAGIC_WALL,                   -1,
757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
758     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
759   },
760   {
761     EL_BD_MAGIC_WALL,                   -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
763     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
764   },
765   {
766     EL_BD_MAGIC_WALL,                   -1,
767     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
768     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
769   },
770   {
771     EL_BD_MAGIC_WALL,                   -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
773     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
774   },
775   {
776     EL_BD_MAGIC_WALL,                   -1,
777     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
778     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
779   },
780
781   {
782     EL_BD_CLOCK,                        -1,
783     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
784     &li.bd_clock_extra_time,            30
785   },
786
787   {
788     EL_BD_VOODOO_DOLL,                  -1,
789     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
790     &li.bd_voodoo_collects_diamonds,    FALSE
791   },
792   {
793     EL_BD_VOODOO_DOLL,                  -1,
794     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
795     &li.bd_voodoo_hurt_kills_player,    FALSE
796   },
797   {
798     EL_BD_VOODOO_DOLL,                  -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
800     &li.bd_voodoo_dies_by_rock,         FALSE
801   },
802   {
803     EL_BD_VOODOO_DOLL,                  -1,
804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
805     &li.bd_voodoo_vanish_by_explosion,  TRUE
806   },
807   {
808     EL_BD_VOODOO_DOLL,                  -1,
809     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
810     &li.bd_voodoo_penalty_time,         30
811   },
812
813   {
814     EL_BD_SLIME,                        -1,
815     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
816     &li.bd_slime_is_predictable,        TRUE
817   },
818   {
819     EL_BD_SLIME,                        -1,
820     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
821     &li.bd_slime_permeability_rate,     100
822   },
823   {
824     EL_BD_SLIME,                        -1,
825     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
826     &li.bd_slime_permeability_bits_c64, 0
827   },
828   {
829     EL_BD_SLIME,                        -1,
830     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
831     &li.bd_slime_random_seed_c64,       -1
832   },
833   {
834     EL_BD_SLIME,                        -1,
835     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
836     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
837   },
838   {
839     EL_BD_SLIME,                        -1,
840     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
841     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
842   },
843   {
844     EL_BD_SLIME,                        -1,
845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
846     &li.bd_slime_eats_element_2,        EL_BD_ROCK
847   },
848   {
849     EL_BD_SLIME,                        -1,
850     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
851     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
852   },
853   {
854     EL_BD_SLIME,                        -1,
855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
856     &li.bd_slime_eats_element_3,        EL_BD_NUT
857   },
858   {
859     EL_BD_SLIME,                        -1,
860     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
861     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
862   },
863
864   {
865     EL_BD_ACID,                         -1,
866     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
867     &li.bd_acid_eats_element,           EL_BD_SAND
868   },
869   {
870     EL_BD_ACID,                         -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
872     &li.bd_acid_spread_rate,            3
873   },
874   {
875     EL_BD_ACID,                         -1,
876     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
877     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
878   },
879
880   {
881     EL_BD_BITER,                        -1,
882     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
883     &li.bd_biter_move_delay,            0
884   },
885   {
886     EL_BD_BITER,                        -1,
887     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
888     &li.bd_biter_eats_element,          EL_BD_DIAMOND
889   },
890
891   {
892     EL_BD_BLADDER,                      -1,
893     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
894     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
895   },
896
897   {
898     EL_BD_EXPANDABLE_WALL_ANY,          -1,
899     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
900     &li.bd_change_expanding_wall,       FALSE
901   },
902   {
903     EL_BD_EXPANDABLE_WALL_ANY,          -1,
904     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
905     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
906   },
907
908   {
909     EL_BD_REPLICATOR,                   -1,
910     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
911     &li.bd_replicators_active,          TRUE
912   },
913   {
914     EL_BD_REPLICATOR,                   -1,
915     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
916     &li.bd_replicator_create_delay,     4
917   },
918
919   {
920     EL_BD_CONVEYOR_LEFT,                -1,
921     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
922     &li.bd_conveyor_belts_active,       TRUE
923   },
924   {
925     EL_BD_CONVEYOR_LEFT,                -1,
926     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
927     &li.bd_conveyor_belts_changed,      FALSE
928   },
929
930   {
931     EL_BD_WATER,                        -1,
932     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
933     &li.bd_water_cannot_flow_down,      FALSE
934   },
935
936   {
937     EL_BD_NUT,                          -1,
938     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
939     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
940   },
941
942   {
943     EL_BD_PNEUMATIC_HAMMER,             -1,
944     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
945     &li.bd_hammer_walls_break_delay,    5
946   },
947   {
948     EL_BD_PNEUMATIC_HAMMER,             -1,
949     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
950     &li.bd_hammer_walls_reappear,       FALSE
951   },
952   {
953     EL_BD_PNEUMATIC_HAMMER,             -1,
954     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
955     &li.bd_hammer_walls_reappear_delay, 100
956   },
957
958   {
959     EL_BD_ROCKET_LAUNCHER,              -1,
960     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
961     &li.bd_infinite_rockets,            FALSE
962   },
963
964   {
965     EL_BD_SKELETON,                     -1,
966     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
967     &li.bd_num_skeletons_needed_for_pot, 5
968   },
969   {
970     EL_BD_SKELETON,                     -1,
971     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
972     &li.bd_skeleton_worth_num_diamonds, 0
973   },
974
975   {
976     EL_BD_CREATURE_SWITCH,              -1,
977     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
978     &li.bd_creatures_start_backwards,   FALSE
979   },
980   {
981     EL_BD_CREATURE_SWITCH,              -1,
982     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
983     &li.bd_creatures_turn_on_hatching,  FALSE
984   },
985   {
986     EL_BD_CREATURE_SWITCH,              -1,
987     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
988     &li.bd_creatures_auto_turn_delay,   0
989   },
990
991   {
992     EL_BD_GRAVITY_SWITCH,               -1,
993     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
994     &li.bd_gravity_direction,           GD_MV_DOWN
995   },
996   {
997     EL_BD_GRAVITY_SWITCH,               -1,
998     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
999     &li.bd_gravity_switch_active,       FALSE
1000   },
1001   {
1002     EL_BD_GRAVITY_SWITCH,               -1,
1003     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1004     &li.bd_gravity_switch_delay,        10
1005   },
1006   {
1007     EL_BD_GRAVITY_SWITCH,               -1,
1008     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1009     &li.bd_gravity_affects_all,         TRUE
1010   },
1011
1012   // (the following values are related to various game elements)
1013
1014   {
1015     EL_EMERALD,                         -1,
1016     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1017     &li.score[SC_EMERALD],              10
1018   },
1019
1020   {
1021     EL_DIAMOND,                         -1,
1022     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1023     &li.score[SC_DIAMOND],              10
1024   },
1025
1026   {
1027     EL_BUG,                             -1,
1028     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1029     &li.score[SC_BUG],                  10
1030   },
1031
1032   {
1033     EL_SPACESHIP,                       -1,
1034     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1035     &li.score[SC_SPACESHIP],            10
1036   },
1037
1038   {
1039     EL_PACMAN,                          -1,
1040     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1041     &li.score[SC_PACMAN],               10
1042   },
1043
1044   {
1045     EL_NUT,                             -1,
1046     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1047     &li.score[SC_NUT],                  10
1048   },
1049
1050   {
1051     EL_DYNAMITE,                        -1,
1052     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1053     &li.score[SC_DYNAMITE],             10
1054   },
1055
1056   {
1057     EL_KEY_1,                           -1,
1058     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1059     &li.score[SC_KEY],                  10
1060   },
1061
1062   {
1063     EL_PEARL,                           -1,
1064     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1065     &li.score[SC_PEARL],                10
1066   },
1067
1068   {
1069     EL_CRYSTAL,                         -1,
1070     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1071     &li.score[SC_CRYSTAL],              10
1072   },
1073
1074   // (amoeba values used by R'n'D game engine only)
1075   {
1076     EL_BD_AMOEBA,                       -1,
1077     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1078     &li.amoeba_content,                 EL_DIAMOND
1079   },
1080   {
1081     EL_BD_AMOEBA,                       -1,
1082     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1083     &li.amoeba_speed,                   10
1084   },
1085   {
1086     EL_BD_AMOEBA,                       -1,
1087     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1088     &li.grow_into_diggable,             TRUE
1089   },
1090   // (amoeba values used by BD game engine only)
1091   {
1092     EL_BD_AMOEBA,                       -1,
1093     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1094     &li.bd_amoeba_wait_for_hatching,    FALSE
1095   },
1096   {
1097     EL_BD_AMOEBA,                       -1,
1098     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1099     &li.bd_amoeba_start_immediately,    TRUE
1100   },
1101   {
1102     EL_BD_AMOEBA,                       -1,
1103     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1104     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1105   },
1106   {
1107     EL_BD_AMOEBA,                       -1,
1108     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1109     &li.bd_amoeba_threshold_too_big,    200
1110   },
1111   {
1112     EL_BD_AMOEBA,                       -1,
1113     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1114     &li.bd_amoeba_slow_growth_time,     200
1115   },
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1119     &li.bd_amoeba_slow_growth_rate,     3
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1124     &li.bd_amoeba_fast_growth_rate,     25
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1129     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1130   },
1131   {
1132     EL_BD_AMOEBA,                       -1,
1133     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1134     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1135   },
1136
1137   {
1138     EL_BD_AMOEBA_2,                     -1,
1139     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1140     &li.bd_amoeba_2_threshold_too_big,  200
1141   },
1142   {
1143     EL_BD_AMOEBA_2,                     -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1145     &li.bd_amoeba_2_slow_growth_time,   200
1146   },
1147   {
1148     EL_BD_AMOEBA_2,                     -1,
1149     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1150     &li.bd_amoeba_2_slow_growth_rate,   3
1151   },
1152   {
1153     EL_BD_AMOEBA_2,                     -1,
1154     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1155     &li.bd_amoeba_2_fast_growth_rate,   25
1156   },
1157   {
1158     EL_BD_AMOEBA_2,                     -1,
1159     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1160     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1161   },
1162   {
1163     EL_BD_AMOEBA_2,                     -1,
1164     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1165     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1166   },
1167   {
1168     EL_BD_AMOEBA_2,                     -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1170     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1171   },
1172   {
1173     EL_BD_AMOEBA_2,                     -1,
1174     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1175     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1176   },
1177
1178   {
1179     EL_YAMYAM,                          -1,
1180     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1181     &li.yamyam_content,                 EL_ROCK, NULL,
1182     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1183   },
1184   {
1185     EL_YAMYAM,                          -1,
1186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1187     &li.score[SC_YAMYAM],               10
1188   },
1189
1190   {
1191     EL_ROBOT,                           -1,
1192     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1193     &li.score[SC_ROBOT],                10
1194   },
1195   {
1196     EL_ROBOT,                           -1,
1197     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1198     &li.slurp_score,                    10
1199   },
1200
1201   {
1202     EL_ROBOT_WHEEL,                     -1,
1203     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1204     &li.time_wheel,                     10
1205   },
1206
1207   {
1208     EL_MAGIC_WALL,                      -1,
1209     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1210     &li.time_magic_wall,                10
1211   },
1212
1213   {
1214     EL_GAME_OF_LIFE,                    -1,
1215     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1216     &li.game_of_life[0],                2
1217   },
1218   {
1219     EL_GAME_OF_LIFE,                    -1,
1220     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1221     &li.game_of_life[1],                3
1222   },
1223   {
1224     EL_GAME_OF_LIFE,                    -1,
1225     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1226     &li.game_of_life[2],                3
1227   },
1228   {
1229     EL_GAME_OF_LIFE,                    -1,
1230     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1231     &li.game_of_life[3],                3
1232   },
1233   {
1234     EL_GAME_OF_LIFE,                    -1,
1235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1236     &li.use_life_bugs,                  FALSE
1237   },
1238
1239   {
1240     EL_BIOMAZE,                         -1,
1241     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1242     &li.biomaze[0],                     2
1243   },
1244   {
1245     EL_BIOMAZE,                         -1,
1246     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1247     &li.biomaze[1],                     3
1248   },
1249   {
1250     EL_BIOMAZE,                         -1,
1251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1252     &li.biomaze[2],                     3
1253   },
1254   {
1255     EL_BIOMAZE,                         -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1257     &li.biomaze[3],                     3
1258   },
1259
1260   {
1261     EL_TIMEGATE_SWITCH,                 -1,
1262     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1263     &li.time_timegate,                  10
1264   },
1265
1266   {
1267     EL_LIGHT_SWITCH_ACTIVE,             -1,
1268     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1269     &li.time_light,                     10
1270   },
1271
1272   {
1273     EL_SHIELD_NORMAL,                   -1,
1274     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1275     &li.shield_normal_time,             10
1276   },
1277   {
1278     EL_SHIELD_NORMAL,                   -1,
1279     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1280     &li.score[SC_SHIELD],               10
1281   },
1282
1283   {
1284     EL_SHIELD_DEADLY,                   -1,
1285     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1286     &li.shield_deadly_time,             10
1287   },
1288   {
1289     EL_SHIELD_DEADLY,                   -1,
1290     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1291     &li.score[SC_SHIELD],               10
1292   },
1293
1294   {
1295     EL_EXTRA_TIME,                      -1,
1296     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1297     &li.extra_time,                     10
1298   },
1299   {
1300     EL_EXTRA_TIME,                      -1,
1301     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1302     &li.extra_time_score,               10
1303   },
1304
1305   {
1306     EL_TIME_ORB_FULL,                   -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_orb_time,                  10
1309   },
1310   {
1311     EL_TIME_ORB_FULL,                   -1,
1312     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1313     &li.use_time_orb_bug,               FALSE
1314   },
1315
1316   {
1317     EL_SPRING,                          -1,
1318     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1319     &li.use_spring_bug,                 FALSE
1320   },
1321
1322   {
1323     EL_EMC_ANDROID,                     -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1325     &li.android_move_time,              10
1326   },
1327   {
1328     EL_EMC_ANDROID,                     -1,
1329     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1330     &li.android_clone_time,             10
1331   },
1332   {
1333     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1334     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1335     &li.android_clone_element[0],       EL_EMPTY, NULL,
1336     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1337   },
1338   {
1339     EL_EMC_ANDROID,                     -1,
1340     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1341     &li.android_clone_element[0],       EL_EMPTY, NULL,
1342     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1343   },
1344
1345   {
1346     EL_EMC_LENSES,                      -1,
1347     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1348     &li.lenses_score,                   10
1349   },
1350   {
1351     EL_EMC_LENSES,                      -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1353     &li.lenses_time,                    10
1354   },
1355
1356   {
1357     EL_EMC_MAGNIFIER,                   -1,
1358     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1359     &li.magnify_score,                  10
1360   },
1361   {
1362     EL_EMC_MAGNIFIER,                   -1,
1363     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1364     &li.magnify_time,                   10
1365   },
1366
1367   {
1368     EL_EMC_MAGIC_BALL,                  -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.ball_time,                      10
1371   },
1372   {
1373     EL_EMC_MAGIC_BALL,                  -1,
1374     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1375     &li.ball_random,                    FALSE
1376   },
1377   {
1378     EL_EMC_MAGIC_BALL,                  -1,
1379     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1380     &li.ball_active_initial,            FALSE
1381   },
1382   {
1383     EL_EMC_MAGIC_BALL,                  -1,
1384     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1385     &li.ball_content,                   EL_EMPTY, NULL,
1386     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1387   },
1388
1389   {
1390     EL_SOKOBAN_FIELD_EMPTY,             -1,
1391     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1392     &li.sb_fields_needed,               TRUE
1393   },
1394
1395   {
1396     EL_SOKOBAN_OBJECT,                  -1,
1397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1398     &li.sb_objects_needed,              TRUE
1399   },
1400
1401   {
1402     EL_MM_MCDUFFIN,                     -1,
1403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1404     &li.mm_laser_red,                   FALSE
1405   },
1406   {
1407     EL_MM_MCDUFFIN,                     -1,
1408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1409     &li.mm_laser_green,                 FALSE
1410   },
1411   {
1412     EL_MM_MCDUFFIN,                     -1,
1413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1414     &li.mm_laser_blue,                  TRUE
1415   },
1416
1417   {
1418     EL_DF_LASER,                        -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.df_laser_red,                   TRUE
1421   },
1422   {
1423     EL_DF_LASER,                        -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.df_laser_green,                 TRUE
1426   },
1427   {
1428     EL_DF_LASER,                        -1,
1429     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1430     &li.df_laser_blue,                  FALSE
1431   },
1432
1433   {
1434     EL_MM_FUSE_ACTIVE,                  -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1436     &li.mm_time_fuse,                   25
1437   },
1438   {
1439     EL_MM_BOMB,                         -1,
1440     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1441     &li.mm_time_bomb,                   75
1442   },
1443
1444   {
1445     EL_MM_GRAY_BALL,                    -1,
1446     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1447     &li.mm_time_ball,                   75
1448   },
1449   {
1450     EL_MM_GRAY_BALL,                    -1,
1451     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1452     &li.mm_ball_choice_mode,            ANIM_RANDOM
1453   },
1454   {
1455     EL_MM_GRAY_BALL,                    -1,
1456     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1457     &li.mm_ball_content,                EL_EMPTY, NULL,
1458     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1459   },
1460   {
1461     EL_MM_GRAY_BALL,                    -1,
1462     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1463     &li.rotate_mm_ball_content,         TRUE
1464   },
1465   {
1466     EL_MM_GRAY_BALL,                    -1,
1467     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1468     &li.explode_mm_ball,                FALSE
1469   },
1470
1471   {
1472     EL_MM_STEEL_BLOCK,                  -1,
1473     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1474     &li.mm_time_block,                  75
1475   },
1476   {
1477     EL_MM_LIGHTBALL,                    -1,
1478     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1479     &li.score[SC_ELEM_BONUS],           10
1480   },
1481
1482   {
1483     -1,                                 -1,
1484     -1,                                 -1,
1485     NULL,                               -1
1486   }
1487 };
1488
1489 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1490 {
1491   {
1492     -1,                                 -1,
1493     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1494     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1495   },
1496   {
1497     -1,                                 -1,
1498     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1499     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1500   },
1501
1502   {
1503     -1,                                 -1,
1504     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1505     &xx_envelope.autowrap,              FALSE
1506   },
1507   {
1508     -1,                                 -1,
1509     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1510     &xx_envelope.centered,              FALSE
1511   },
1512
1513   {
1514     -1,                                 -1,
1515     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1516     &xx_envelope.text,                  -1, NULL,
1517     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1518     &xx_default_string_empty[0]
1519   },
1520
1521   {
1522     -1,                                 -1,
1523     -1,                                 -1,
1524     NULL,                               -1
1525   }
1526 };
1527
1528 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1529 {
1530   {
1531     -1,                                 -1,
1532     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1533     &xx_ei.description[0],              -1,
1534     &yy_ei.description[0],
1535     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1536     &xx_default_description[0]
1537   },
1538
1539   {
1540     -1,                                 -1,
1541     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1542     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1543     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1544   },
1545 #if ENABLE_RESERVED_CODE
1546   // (reserved for later use)
1547   {
1548     -1,                                 -1,
1549     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1550     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1551     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1552   },
1553 #endif
1554
1555   {
1556     -1,                                 -1,
1557     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1558     &xx_ei.use_gfx_element,             FALSE,
1559     &yy_ei.use_gfx_element
1560   },
1561   {
1562     -1,                                 -1,
1563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1564     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1565     &yy_ei.gfx_element_initial
1566   },
1567
1568   {
1569     -1,                                 -1,
1570     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1571     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1572     &yy_ei.access_direction
1573   },
1574
1575   {
1576     -1,                                 -1,
1577     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1578     &xx_ei.collect_score_initial,       10,
1579     &yy_ei.collect_score_initial
1580   },
1581   {
1582     -1,                                 -1,
1583     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1584     &xx_ei.collect_count_initial,       1,
1585     &yy_ei.collect_count_initial
1586   },
1587
1588   {
1589     -1,                                 -1,
1590     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1591     &xx_ei.ce_value_fixed_initial,      0,
1592     &yy_ei.ce_value_fixed_initial
1593   },
1594   {
1595     -1,                                 -1,
1596     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1597     &xx_ei.ce_value_random_initial,     0,
1598     &yy_ei.ce_value_random_initial
1599   },
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1603     &xx_ei.use_last_ce_value,           FALSE,
1604     &yy_ei.use_last_ce_value
1605   },
1606
1607   {
1608     -1,                                 -1,
1609     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1610     &xx_ei.push_delay_fixed,            8,
1611     &yy_ei.push_delay_fixed
1612   },
1613   {
1614     -1,                                 -1,
1615     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1616     &xx_ei.push_delay_random,           8,
1617     &yy_ei.push_delay_random
1618   },
1619   {
1620     -1,                                 -1,
1621     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1622     &xx_ei.drop_delay_fixed,            0,
1623     &yy_ei.drop_delay_fixed
1624   },
1625   {
1626     -1,                                 -1,
1627     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1628     &xx_ei.drop_delay_random,           0,
1629     &yy_ei.drop_delay_random
1630   },
1631   {
1632     -1,                                 -1,
1633     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1634     &xx_ei.move_delay_fixed,            0,
1635     &yy_ei.move_delay_fixed
1636   },
1637   {
1638     -1,                                 -1,
1639     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1640     &xx_ei.move_delay_random,           0,
1641     &yy_ei.move_delay_random
1642   },
1643   {
1644     -1,                                 -1,
1645     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1646     &xx_ei.step_delay_fixed,            0,
1647     &yy_ei.step_delay_fixed
1648   },
1649   {
1650     -1,                                 -1,
1651     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1652     &xx_ei.step_delay_random,           0,
1653     &yy_ei.step_delay_random
1654   },
1655
1656   {
1657     -1,                                 -1,
1658     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1659     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1660     &yy_ei.move_pattern
1661   },
1662   {
1663     -1,                                 -1,
1664     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1665     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1666     &yy_ei.move_direction_initial
1667   },
1668   {
1669     -1,                                 -1,
1670     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1671     &xx_ei.move_stepsize,               TILEX / 8,
1672     &yy_ei.move_stepsize
1673   },
1674
1675   {
1676     -1,                                 -1,
1677     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1678     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1679     &yy_ei.move_enter_element
1680   },
1681   {
1682     -1,                                 -1,
1683     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1684     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1685     &yy_ei.move_leave_element
1686   },
1687   {
1688     -1,                                 -1,
1689     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1690     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1691     &yy_ei.move_leave_type
1692   },
1693
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1697     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1698     &yy_ei.slippery_type
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1704     &xx_ei.explosion_type,              EXPLODES_3X3,
1705     &yy_ei.explosion_type
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1710     &xx_ei.explosion_delay,             16,
1711     &yy_ei.explosion_delay
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1716     &xx_ei.ignition_delay,              8,
1717     &yy_ei.ignition_delay
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1723     &xx_ei.content,                     EL_EMPTY_SPACE,
1724     &yy_ei.content,
1725     &xx_num_contents,                   1, 1
1726   },
1727
1728   // ---------- "num_change_pages" must be the last entry ---------------------
1729
1730   {
1731     -1,                                 SAVE_CONF_ALWAYS,
1732     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1733     &xx_ei.num_change_pages,            1,
1734     &yy_ei.num_change_pages
1735   },
1736
1737   {
1738     -1,                                 -1,
1739     -1,                                 -1,
1740     NULL,                               -1,
1741     NULL
1742   }
1743 };
1744
1745 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1746 {
1747   // ---------- "current_change_page" must be the first entry -----------------
1748
1749   {
1750     -1,                                 SAVE_CONF_ALWAYS,
1751     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1752     &xx_current_change_page,            -1
1753   },
1754
1755   // ---------- (the remaining entries can be in any order) -------------------
1756
1757   {
1758     -1,                                 -1,
1759     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1760     &xx_change.can_change,              FALSE
1761   },
1762
1763   {
1764     -1,                                 -1,
1765     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1766     &xx_event_bits[0],                  0
1767   },
1768   {
1769     -1,                                 -1,
1770     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1771     &xx_event_bits[1],                  0
1772   },
1773
1774   {
1775     -1,                                 -1,
1776     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1777     &xx_change.trigger_player,          CH_PLAYER_ANY
1778   },
1779   {
1780     -1,                                 -1,
1781     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1782     &xx_change.trigger_side,            CH_SIDE_ANY
1783   },
1784   {
1785     -1,                                 -1,
1786     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1787     &xx_change.trigger_page,            CH_PAGE_ANY
1788   },
1789
1790   {
1791     -1,                                 -1,
1792     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1793     &xx_change.target_element,          EL_EMPTY_SPACE
1794   },
1795
1796   {
1797     -1,                                 -1,
1798     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1799     &xx_change.delay_fixed,             0
1800   },
1801   {
1802     -1,                                 -1,
1803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1804     &xx_change.delay_random,            0
1805   },
1806   {
1807     -1,                                 -1,
1808     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1809     &xx_change.delay_frames,            FRAMES_PER_SECOND
1810   },
1811
1812   {
1813     -1,                                 -1,
1814     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1815     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1816   },
1817
1818   {
1819     -1,                                 -1,
1820     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1821     &xx_change.explode,                 FALSE
1822   },
1823   {
1824     -1,                                 -1,
1825     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1826     &xx_change.use_target_content,      FALSE
1827   },
1828   {
1829     -1,                                 -1,
1830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1831     &xx_change.only_if_complete,        FALSE
1832   },
1833   {
1834     -1,                                 -1,
1835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1836     &xx_change.use_random_replace,      FALSE
1837   },
1838   {
1839     -1,                                 -1,
1840     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1841     &xx_change.random_percentage,       100
1842   },
1843   {
1844     -1,                                 -1,
1845     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1846     &xx_change.replace_when,            CP_WHEN_EMPTY
1847   },
1848
1849   {
1850     -1,                                 -1,
1851     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1852     &xx_change.has_action,              FALSE
1853   },
1854   {
1855     -1,                                 -1,
1856     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1857     &xx_change.action_type,             CA_NO_ACTION
1858   },
1859   {
1860     -1,                                 -1,
1861     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1862     &xx_change.action_mode,             CA_MODE_UNDEFINED
1863   },
1864   {
1865     -1,                                 -1,
1866     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1867     &xx_change.action_arg,              CA_ARG_UNDEFINED
1868   },
1869
1870   {
1871     -1,                                 -1,
1872     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1873     &xx_change.action_element,          EL_EMPTY_SPACE
1874   },
1875
1876   {
1877     -1,                                 -1,
1878     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1879     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1880     &xx_num_contents,                   1, 1
1881   },
1882
1883   {
1884     -1,                                 -1,
1885     -1,                                 -1,
1886     NULL,                               -1
1887   }
1888 };
1889
1890 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1891 {
1892   {
1893     -1,                                 -1,
1894     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1895     &xx_ei.description[0],              -1, NULL,
1896     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1897     &xx_default_description[0]
1898   },
1899
1900   {
1901     -1,                                 -1,
1902     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1903     &xx_ei.use_gfx_element,             FALSE
1904   },
1905   {
1906     -1,                                 -1,
1907     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1908     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1909   },
1910
1911   {
1912     -1,                                 -1,
1913     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1914     &xx_group.choice_mode,              ANIM_RANDOM
1915   },
1916
1917   {
1918     -1,                                 -1,
1919     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1920     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1921     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1922   },
1923
1924   {
1925     -1,                                 -1,
1926     -1,                                 -1,
1927     NULL,                               -1
1928   }
1929 };
1930
1931 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1932 {
1933   {
1934     -1,                                 -1,
1935     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1936     &xx_ei.use_gfx_element,             FALSE
1937   },
1938   {
1939     -1,                                 -1,
1940     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1941     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1942   },
1943
1944   {
1945     -1,                                 -1,
1946     -1,                                 -1,
1947     NULL,                               -1
1948   }
1949 };
1950
1951 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1952 {
1953   {
1954     EL_PLAYER_1,                        -1,
1955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1956     &li.block_snap_field,               TRUE
1957   },
1958   {
1959     EL_PLAYER_1,                        -1,
1960     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1961     &li.continuous_snapping,            TRUE
1962   },
1963   {
1964     EL_PLAYER_1,                        -1,
1965     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1966     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1967   },
1968   {
1969     EL_PLAYER_1,                        -1,
1970     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1971     &li.use_start_element[0],           FALSE
1972   },
1973   {
1974     EL_PLAYER_1,                        -1,
1975     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1976     &li.start_element[0],               EL_PLAYER_1
1977   },
1978   {
1979     EL_PLAYER_1,                        -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1981     &li.use_artwork_element[0],         FALSE
1982   },
1983   {
1984     EL_PLAYER_1,                        -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1986     &li.artwork_element[0],             EL_PLAYER_1
1987   },
1988   {
1989     EL_PLAYER_1,                        -1,
1990     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1991     &li.use_explosion_element[0],       FALSE
1992   },
1993   {
1994     EL_PLAYER_1,                        -1,
1995     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1996     &li.explosion_element[0],           EL_PLAYER_1
1997   },
1998
1999   {
2000     -1,                                 -1,
2001     -1,                                 -1,
2002     NULL,                               -1
2003   }
2004 };
2005
2006 static struct
2007 {
2008   int filetype;
2009   char *id;
2010 }
2011 filetype_id_list[] =
2012 {
2013   { LEVEL_FILE_TYPE_RND,        "RND"   },
2014   { LEVEL_FILE_TYPE_BD,         "BD"    },
2015   { LEVEL_FILE_TYPE_EM,         "EM"    },
2016   { LEVEL_FILE_TYPE_SP,         "SP"    },
2017   { LEVEL_FILE_TYPE_DX,         "DX"    },
2018   { LEVEL_FILE_TYPE_SB,         "SB"    },
2019   { LEVEL_FILE_TYPE_DC,         "DC"    },
2020   { LEVEL_FILE_TYPE_MM,         "MM"    },
2021   { LEVEL_FILE_TYPE_MM,         "DF"    },
2022   { -1,                         NULL    },
2023 };
2024
2025
2026 // ============================================================================
2027 // level file functions
2028 // ============================================================================
2029
2030 static boolean check_special_flags(char *flag)
2031 {
2032   if (strEqual(options.special_flags, flag) ||
2033       strEqual(leveldir_current->special_flags, flag))
2034     return TRUE;
2035
2036   return FALSE;
2037 }
2038
2039 static struct DateInfo getCurrentDate(void)
2040 {
2041   time_t epoch_seconds = time(NULL);
2042   struct tm *now = localtime(&epoch_seconds);
2043   struct DateInfo date;
2044
2045   date.year  = now->tm_year + 1900;
2046   date.month = now->tm_mon  + 1;
2047   date.day   = now->tm_mday;
2048
2049   date.src   = DATE_SRC_CLOCK;
2050
2051   return date;
2052 }
2053
2054 static void resetEventFlags(struct ElementChangeInfo *change)
2055 {
2056   int i;
2057
2058   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2059     change->has_event[i] = FALSE;
2060 }
2061
2062 static void resetEventBits(void)
2063 {
2064   int i;
2065
2066   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2067     xx_event_bits[i] = 0;
2068 }
2069
2070 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2071 {
2072   int i;
2073
2074   /* important: only change event flag if corresponding event bit is set
2075      (this is because all xx_event_bits[] values are loaded separately,
2076      and all xx_event_bits[] values are set back to zero before loading
2077      another value xx_event_bits[x] (each value representing 32 flags)) */
2078
2079   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2080     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2081       change->has_event[i] = TRUE;
2082 }
2083
2084 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2085 {
2086   int i;
2087
2088   /* in contrast to the above function setEventFlagsFromEventBits(), it
2089      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2090      depending on the corresponding change->has_event[i] values here, as
2091      all xx_event_bits[] values are reset in resetEventBits() before */
2092
2093   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2094     if (change->has_event[i])
2095       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2096 }
2097
2098 static char *getDefaultElementDescription(struct ElementInfo *ei)
2099 {
2100   static char description[MAX_ELEMENT_NAME_LEN + 1];
2101   char *default_description = (ei->custom_description != NULL ?
2102                                ei->custom_description :
2103                                ei->editor_description);
2104   int i;
2105
2106   // always start with reliable default values
2107   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2108     description[i] = '\0';
2109
2110   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2111   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2112
2113   return &description[0];
2114 }
2115
2116 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2117 {
2118   char *default_description = getDefaultElementDescription(ei);
2119   int i;
2120
2121   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2122     ei->description[i] = default_description[i];
2123 }
2124
2125 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2126 {
2127   int i;
2128
2129   for (i = 0; conf[i].data_type != -1; i++)
2130   {
2131     int default_value = conf[i].default_value;
2132     int data_type = conf[i].data_type;
2133     int conf_type = conf[i].conf_type;
2134     int byte_mask = conf_type & CONF_MASK_BYTES;
2135
2136     if (byte_mask == CONF_MASK_MULTI_BYTES)
2137     {
2138       int default_num_entities = conf[i].default_num_entities;
2139       int max_num_entities = conf[i].max_num_entities;
2140
2141       *(int *)(conf[i].num_entities) = default_num_entities;
2142
2143       if (data_type == TYPE_STRING)
2144       {
2145         char *default_string = conf[i].default_string;
2146         char *string = (char *)(conf[i].value);
2147
2148         strncpy(string, default_string, max_num_entities);
2149       }
2150       else if (data_type == TYPE_ELEMENT_LIST)
2151       {
2152         int *element_array = (int *)(conf[i].value);
2153         int j;
2154
2155         for (j = 0; j < max_num_entities; j++)
2156           element_array[j] = default_value;
2157       }
2158       else if (data_type == TYPE_CONTENT_LIST)
2159       {
2160         struct Content *content = (struct Content *)(conf[i].value);
2161         int c, x, y;
2162
2163         for (c = 0; c < max_num_entities; c++)
2164           for (y = 0; y < 3; y++)
2165             for (x = 0; x < 3; x++)
2166               content[c].e[x][y] = default_value;
2167       }
2168     }
2169     else        // constant size configuration data (1, 2 or 4 bytes)
2170     {
2171       if (data_type == TYPE_BOOLEAN)
2172         *(boolean *)(conf[i].value) = default_value;
2173       else
2174         *(int *)    (conf[i].value) = default_value;
2175     }
2176   }
2177 }
2178
2179 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2180 {
2181   int i;
2182
2183   for (i = 0; conf[i].data_type != -1; i++)
2184   {
2185     int data_type = conf[i].data_type;
2186     int conf_type = conf[i].conf_type;
2187     int byte_mask = conf_type & CONF_MASK_BYTES;
2188
2189     if (byte_mask == CONF_MASK_MULTI_BYTES)
2190     {
2191       int max_num_entities = conf[i].max_num_entities;
2192
2193       if (data_type == TYPE_STRING)
2194       {
2195         char *string      = (char *)(conf[i].value);
2196         char *string_copy = (char *)(conf[i].value_copy);
2197
2198         strncpy(string_copy, string, max_num_entities);
2199       }
2200       else if (data_type == TYPE_ELEMENT_LIST)
2201       {
2202         int *element_array      = (int *)(conf[i].value);
2203         int *element_array_copy = (int *)(conf[i].value_copy);
2204         int j;
2205
2206         for (j = 0; j < max_num_entities; j++)
2207           element_array_copy[j] = element_array[j];
2208       }
2209       else if (data_type == TYPE_CONTENT_LIST)
2210       {
2211         struct Content *content      = (struct Content *)(conf[i].value);
2212         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2213         int c, x, y;
2214
2215         for (c = 0; c < max_num_entities; c++)
2216           for (y = 0; y < 3; y++)
2217             for (x = 0; x < 3; x++)
2218               content_copy[c].e[x][y] = content[c].e[x][y];
2219       }
2220     }
2221     else        // constant size configuration data (1, 2 or 4 bytes)
2222     {
2223       if (data_type == TYPE_BOOLEAN)
2224         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2225       else
2226         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2227     }
2228   }
2229 }
2230
2231 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2232 {
2233   int i;
2234
2235   xx_ei = *ei_from;     // copy element data into temporary buffer
2236   yy_ei = *ei_to;       // copy element data into temporary buffer
2237
2238   copyConfigFromConfigList(chunk_config_CUSX_base);
2239
2240   *ei_from = xx_ei;
2241   *ei_to   = yy_ei;
2242
2243   // ---------- reinitialize and copy change pages ----------
2244
2245   ei_to->num_change_pages = ei_from->num_change_pages;
2246   ei_to->current_change_page = ei_from->current_change_page;
2247
2248   setElementChangePages(ei_to, ei_to->num_change_pages);
2249
2250   for (i = 0; i < ei_to->num_change_pages; i++)
2251     ei_to->change_page[i] = ei_from->change_page[i];
2252
2253   // ---------- copy group element info ----------
2254   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2255     *ei_to->group = *ei_from->group;
2256
2257   // mark this custom element as modified
2258   ei_to->modified_settings = TRUE;
2259 }
2260
2261 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2262 {
2263   int change_page_size = sizeof(struct ElementChangeInfo);
2264
2265   ei->num_change_pages = MAX(1, change_pages);
2266
2267   ei->change_page =
2268     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2269
2270   if (ei->current_change_page >= ei->num_change_pages)
2271     ei->current_change_page = ei->num_change_pages - 1;
2272
2273   ei->change = &ei->change_page[ei->current_change_page];
2274 }
2275
2276 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2277 {
2278   xx_change = *change;          // copy change data into temporary buffer
2279
2280   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2281
2282   *change = xx_change;
2283
2284   resetEventFlags(change);
2285
2286   change->direct_action = 0;
2287   change->other_action = 0;
2288
2289   change->pre_change_function = NULL;
2290   change->change_function = NULL;
2291   change->post_change_function = NULL;
2292 }
2293
2294 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2295 {
2296   int i, x, y;
2297
2298   li = *level;          // copy level data into temporary buffer
2299   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2300   *level = li;          // copy temporary buffer back to level data
2301
2302   setLevelInfoToDefaults_BD();
2303   setLevelInfoToDefaults_EM();
2304   setLevelInfoToDefaults_SP();
2305   setLevelInfoToDefaults_MM();
2306
2307   level->native_bd_level = &native_bd_level;
2308   level->native_em_level = &native_em_level;
2309   level->native_sp_level = &native_sp_level;
2310   level->native_mm_level = &native_mm_level;
2311
2312   level->file_version = FILE_VERSION_ACTUAL;
2313   level->game_version = GAME_VERSION_ACTUAL;
2314
2315   level->creation_date = getCurrentDate();
2316
2317   level->encoding_16bit_field  = TRUE;
2318   level->encoding_16bit_yamyam = TRUE;
2319   level->encoding_16bit_amoeba = TRUE;
2320
2321   // clear level name and level author string buffers
2322   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2323     level->name[i] = '\0';
2324   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2325     level->author[i] = '\0';
2326
2327   // set level name and level author to default values
2328   strcpy(level->name, NAMELESS_LEVEL_NAME);
2329   strcpy(level->author, ANONYMOUS_NAME);
2330
2331   // set level playfield to playable default level with player and exit
2332   for (x = 0; x < MAX_LEV_FIELDX; x++)
2333     for (y = 0; y < MAX_LEV_FIELDY; y++)
2334       level->field[x][y] = EL_SAND;
2335
2336   level->field[0][0] = EL_PLAYER_1;
2337   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2338
2339   BorderElement = EL_STEELWALL;
2340
2341   // detect custom elements when loading them
2342   level->file_has_custom_elements = FALSE;
2343
2344   // set all bug compatibility flags to "false" => do not emulate this bug
2345   level->use_action_after_change_bug = FALSE;
2346
2347   if (leveldir_current)
2348   {
2349     // try to determine better author name than 'anonymous'
2350     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2351     {
2352       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2353       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2354     }
2355     else
2356     {
2357       switch (LEVELCLASS(leveldir_current))
2358       {
2359         case LEVELCLASS_TUTORIAL:
2360           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2361           break;
2362
2363         case LEVELCLASS_CONTRIB:
2364           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2365           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2366           break;
2367
2368         case LEVELCLASS_PRIVATE:
2369           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2370           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2371           break;
2372
2373         default:
2374           // keep default value
2375           break;
2376       }
2377     }
2378   }
2379 }
2380
2381 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2382 {
2383   static boolean clipboard_elements_initialized = FALSE;
2384   int i;
2385
2386   InitElementPropertiesStatic();
2387
2388   li = *level;          // copy level data into temporary buffer
2389   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2390   *level = li;          // copy temporary buffer back to level data
2391
2392   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2393   {
2394     int element = i;
2395     struct ElementInfo *ei = &element_info[element];
2396
2397     if (element == EL_MM_GRAY_BALL)
2398     {
2399       struct LevelInfo_MM *level_mm = level->native_mm_level;
2400       int j;
2401
2402       for (j = 0; j < level->num_mm_ball_contents; j++)
2403         level->mm_ball_content[j] =
2404           map_element_MM_to_RND(level_mm->ball_content[j]);
2405     }
2406
2407     // never initialize clipboard elements after the very first time
2408     // (to be able to use clipboard elements between several levels)
2409     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2410       continue;
2411
2412     if (IS_ENVELOPE(element))
2413     {
2414       int envelope_nr = element - EL_ENVELOPE_1;
2415
2416       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2417
2418       level->envelope[envelope_nr] = xx_envelope;
2419     }
2420
2421     if (IS_CUSTOM_ELEMENT(element) ||
2422         IS_GROUP_ELEMENT(element) ||
2423         IS_INTERNAL_ELEMENT(element))
2424     {
2425       xx_ei = *ei;      // copy element data into temporary buffer
2426
2427       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2428
2429       *ei = xx_ei;
2430     }
2431
2432     setElementChangePages(ei, 1);
2433     setElementChangeInfoToDefaults(ei->change);
2434
2435     if (IS_CUSTOM_ELEMENT(element) ||
2436         IS_GROUP_ELEMENT(element))
2437     {
2438       setElementDescriptionToDefault(ei);
2439
2440       ei->modified_settings = FALSE;
2441     }
2442
2443     if (IS_CUSTOM_ELEMENT(element) ||
2444         IS_INTERNAL_ELEMENT(element))
2445     {
2446       // internal values used in level editor
2447
2448       ei->access_type = 0;
2449       ei->access_layer = 0;
2450       ei->access_protected = 0;
2451       ei->walk_to_action = 0;
2452       ei->smash_targets = 0;
2453       ei->deadliness = 0;
2454
2455       ei->can_explode_by_fire = FALSE;
2456       ei->can_explode_smashed = FALSE;
2457       ei->can_explode_impact = FALSE;
2458
2459       ei->current_change_page = 0;
2460     }
2461
2462     if (IS_GROUP_ELEMENT(element) ||
2463         IS_INTERNAL_ELEMENT(element))
2464     {
2465       struct ElementGroupInfo *group;
2466
2467       // initialize memory for list of elements in group
2468       if (ei->group == NULL)
2469         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2470
2471       group = ei->group;
2472
2473       xx_group = *group;        // copy group data into temporary buffer
2474
2475       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2476
2477       *group = xx_group;
2478     }
2479
2480     if (IS_EMPTY_ELEMENT(element) ||
2481         IS_INTERNAL_ELEMENT(element))
2482     {
2483       xx_ei = *ei;              // copy element data into temporary buffer
2484
2485       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2486
2487       *ei = xx_ei;
2488     }
2489   }
2490
2491   clipboard_elements_initialized = TRUE;
2492 }
2493
2494 static void setLevelInfoToDefaults(struct LevelInfo *level,
2495                                    boolean level_info_only,
2496                                    boolean reset_file_status)
2497 {
2498   setLevelInfoToDefaults_Level(level);
2499
2500   if (!level_info_only)
2501     setLevelInfoToDefaults_Elements(level);
2502
2503   if (reset_file_status)
2504   {
2505     level->no_valid_file = FALSE;
2506     level->no_level_file = FALSE;
2507   }
2508
2509   level->changed = FALSE;
2510 }
2511
2512 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2513 {
2514   level_file_info->nr = 0;
2515   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2516   level_file_info->packed = FALSE;
2517
2518   setString(&level_file_info->basename, NULL);
2519   setString(&level_file_info->filename, NULL);
2520 }
2521
2522 int getMappedElement_SB(int, boolean);
2523
2524 static void ActivateLevelTemplate(void)
2525 {
2526   int x, y;
2527
2528   if (check_special_flags("load_xsb_to_ces"))
2529   {
2530     // fill smaller playfields with padding "beyond border wall" elements
2531     if (level.fieldx < level_template.fieldx ||
2532         level.fieldy < level_template.fieldy)
2533     {
2534       short field[level.fieldx][level.fieldy];
2535       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2536       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2537       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2538       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2539
2540       // copy old playfield (which is smaller than the visible area)
2541       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2542         field[x][y] = level.field[x][y];
2543
2544       // fill new, larger playfield with "beyond border wall" elements
2545       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2546         level.field[x][y] = getMappedElement_SB('_', TRUE);
2547
2548       // copy the old playfield to the middle of the new playfield
2549       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2550         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2551
2552       level.fieldx = new_fieldx;
2553       level.fieldy = new_fieldy;
2554     }
2555   }
2556
2557   // Currently there is no special action needed to activate the template
2558   // data, because 'element_info' property settings overwrite the original
2559   // level data, while all other variables do not change.
2560
2561   // Exception: 'from_level_template' elements in the original level playfield
2562   // are overwritten with the corresponding elements at the same position in
2563   // playfield from the level template.
2564
2565   for (x = 0; x < level.fieldx; x++)
2566     for (y = 0; y < level.fieldy; y++)
2567       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2568         level.field[x][y] = level_template.field[x][y];
2569
2570   if (check_special_flags("load_xsb_to_ces"))
2571   {
2572     struct LevelInfo level_backup = level;
2573
2574     // overwrite all individual level settings from template level settings
2575     level = level_template;
2576
2577     // restore level file info
2578     level.file_info = level_backup.file_info;
2579
2580     // restore playfield size
2581     level.fieldx = level_backup.fieldx;
2582     level.fieldy = level_backup.fieldy;
2583
2584     // restore playfield content
2585     for (x = 0; x < level.fieldx; x++)
2586       for (y = 0; y < level.fieldy; y++)
2587         level.field[x][y] = level_backup.field[x][y];
2588
2589     // restore name and author from individual level
2590     strcpy(level.name,   level_backup.name);
2591     strcpy(level.author, level_backup.author);
2592
2593     // restore flag "use_custom_template"
2594     level.use_custom_template = level_backup.use_custom_template;
2595   }
2596 }
2597
2598 static boolean checkForPackageFromBasename_BD(char *basename)
2599 {
2600   // check for native BD level file extensions
2601   if (!strSuffixLower(basename, ".bd") &&
2602       !strSuffixLower(basename, ".bdr") &&
2603       !strSuffixLower(basename, ".brc") &&
2604       !strSuffixLower(basename, ".gds"))
2605     return FALSE;
2606
2607   // check for standard single-level BD files (like "001.bd")
2608   if (strSuffixLower(basename, ".bd") &&
2609       strlen(basename) == 6 &&
2610       basename[0] >= '0' && basename[0] <= '9' &&
2611       basename[1] >= '0' && basename[1] <= '9' &&
2612       basename[2] >= '0' && basename[2] <= '9')
2613     return FALSE;
2614
2615   // this is a level package in native BD file format
2616   return TRUE;
2617 }
2618
2619 static char *getLevelFilenameFromBasename(char *basename)
2620 {
2621   static char *filename = NULL;
2622
2623   checked_free(filename);
2624
2625   filename = getPath2(getCurrentLevelDir(), basename);
2626
2627   return filename;
2628 }
2629
2630 static int getFileTypeFromBasename(char *basename)
2631 {
2632   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2633
2634   static char *filename = NULL;
2635   struct stat file_status;
2636
2637   // ---------- try to determine file type from filename ----------
2638
2639   // check for typical filename of a Supaplex level package file
2640   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2641     return LEVEL_FILE_TYPE_SP;
2642
2643   // check for typical filename of a Diamond Caves II level package file
2644   if (strSuffixLower(basename, ".dc") ||
2645       strSuffixLower(basename, ".dc2"))
2646     return LEVEL_FILE_TYPE_DC;
2647
2648   // check for typical filename of a Sokoban level package file
2649   if (strSuffixLower(basename, ".xsb") &&
2650       strchr(basename, '%') == NULL)
2651     return LEVEL_FILE_TYPE_SB;
2652
2653   // check for typical filename of a Boulder Dash (GDash) level package file
2654   if (checkForPackageFromBasename_BD(basename))
2655     return LEVEL_FILE_TYPE_BD;
2656
2657   // ---------- try to determine file type from filesize ----------
2658
2659   checked_free(filename);
2660   filename = getPath2(getCurrentLevelDir(), basename);
2661
2662   if (stat(filename, &file_status) == 0)
2663   {
2664     // check for typical filesize of a Supaplex level package file
2665     if (file_status.st_size == 170496)
2666       return LEVEL_FILE_TYPE_SP;
2667   }
2668
2669   return LEVEL_FILE_TYPE_UNKNOWN;
2670 }
2671
2672 static int getFileTypeFromMagicBytes(char *filename, int type)
2673 {
2674   File *file;
2675
2676   if ((file = openFile(filename, MODE_READ)))
2677   {
2678     char chunk_name[CHUNK_ID_LEN + 1];
2679
2680     getFileChunkBE(file, chunk_name, NULL);
2681
2682     if (strEqual(chunk_name, "MMII") ||
2683         strEqual(chunk_name, "MIRR"))
2684       type = LEVEL_FILE_TYPE_MM;
2685
2686     closeFile(file);
2687   }
2688
2689   return type;
2690 }
2691
2692 static boolean checkForPackageFromBasename(char *basename)
2693 {
2694   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2695   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2696
2697   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2698 }
2699
2700 static char *getSingleLevelBasenameExt(int nr, char *extension)
2701 {
2702   static char basename[MAX_FILENAME_LEN];
2703
2704   if (nr < 0)
2705     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2706   else
2707     sprintf(basename, "%03d.%s", nr, extension);
2708
2709   return basename;
2710 }
2711
2712 static char *getSingleLevelBasename(int nr)
2713 {
2714   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2715 }
2716
2717 static char *getPackedLevelBasename(int type)
2718 {
2719   static char basename[MAX_FILENAME_LEN];
2720   char *directory = getCurrentLevelDir();
2721   Directory *dir;
2722   DirectoryEntry *dir_entry;
2723
2724   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2725
2726   if ((dir = openDirectory(directory)) == NULL)
2727   {
2728     Warn("cannot read current level directory '%s'", directory);
2729
2730     return basename;
2731   }
2732
2733   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2734   {
2735     char *entry_basename = dir_entry->basename;
2736     int entry_type = getFileTypeFromBasename(entry_basename);
2737
2738     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2739     {
2740       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2741           type == entry_type)
2742       {
2743         strcpy(basename, entry_basename);
2744
2745         break;
2746       }
2747     }
2748   }
2749
2750   closeDirectory(dir);
2751
2752   return basename;
2753 }
2754
2755 static char *getSingleLevelFilename(int nr)
2756 {
2757   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2758 }
2759
2760 #if ENABLE_UNUSED_CODE
2761 static char *getPackedLevelFilename(int type)
2762 {
2763   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2764 }
2765 #endif
2766
2767 char *getDefaultLevelFilename(int nr)
2768 {
2769   return getSingleLevelFilename(nr);
2770 }
2771
2772 #if ENABLE_UNUSED_CODE
2773 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2774                                                  int type)
2775 {
2776   lfi->type = type;
2777   lfi->packed = FALSE;
2778
2779   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2780   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2781 }
2782 #endif
2783
2784 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2785                                                  int type, char *format, ...)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788   va_list ap;
2789
2790   va_start(ap, format);
2791   vsprintf(basename, format, ap);
2792   va_end(ap);
2793
2794   lfi->type = type;
2795   lfi->packed = FALSE;
2796
2797   setString(&lfi->basename, basename);
2798   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2799 }
2800
2801 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2802                                                  int type)
2803 {
2804   lfi->type = type;
2805   lfi->packed = TRUE;
2806
2807   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2808   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2809 }
2810
2811 static int getFiletypeFromID(char *filetype_id)
2812 {
2813   char *filetype_id_lower;
2814   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2815   int i;
2816
2817   if (filetype_id == NULL)
2818     return LEVEL_FILE_TYPE_UNKNOWN;
2819
2820   filetype_id_lower = getStringToLower(filetype_id);
2821
2822   for (i = 0; filetype_id_list[i].id != NULL; i++)
2823   {
2824     char *id_lower = getStringToLower(filetype_id_list[i].id);
2825     
2826     if (strEqual(filetype_id_lower, id_lower))
2827       filetype = filetype_id_list[i].filetype;
2828
2829     free(id_lower);
2830
2831     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2832       break;
2833   }
2834
2835   free(filetype_id_lower);
2836
2837   return filetype;
2838 }
2839
2840 char *getLocalLevelTemplateFilename(void)
2841 {
2842   return getDefaultLevelFilename(-1);
2843 }
2844
2845 char *getGlobalLevelTemplateFilename(void)
2846 {
2847   // global variable "leveldir_current" must be modified in the loop below
2848   LevelDirTree *leveldir_current_last = leveldir_current;
2849   char *filename = NULL;
2850
2851   // check for template level in path from current to topmost tree node
2852
2853   while (leveldir_current != NULL)
2854   {
2855     filename = getDefaultLevelFilename(-1);
2856
2857     if (fileExists(filename))
2858       break;
2859
2860     leveldir_current = leveldir_current->node_parent;
2861   }
2862
2863   // restore global variable "leveldir_current" modified in above loop
2864   leveldir_current = leveldir_current_last;
2865
2866   return filename;
2867 }
2868
2869 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2870 {
2871   int nr = lfi->nr;
2872
2873   // special case: level number is negative => check for level template file
2874   if (nr < 0)
2875   {
2876     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2877                                          getSingleLevelBasename(-1));
2878
2879     // replace local level template filename with global template filename
2880     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2881
2882     // no fallback if template file not existing
2883     return;
2884   }
2885
2886   // special case: check for file name/pattern specified in "levelinfo.conf"
2887   if (leveldir_current->level_filename != NULL)
2888   {
2889     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2890
2891     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2892                                          leveldir_current->level_filename, nr);
2893
2894     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2895
2896     if (fileExists(lfi->filename))
2897       return;
2898   }
2899   else if (leveldir_current->level_filetype != NULL)
2900   {
2901     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2902
2903     // check for specified native level file with standard file name
2904     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2905                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2906     if (fileExists(lfi->filename))
2907       return;
2908   }
2909
2910   // check for native Rocks'n'Diamonds level file
2911   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2912                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2913   if (fileExists(lfi->filename))
2914     return;
2915
2916   // check for native Boulder Dash level file
2917   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2918   if (fileExists(lfi->filename))
2919     return;
2920
2921   // check for Emerald Mine level file (V1)
2922   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2923                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2924   if (fileExists(lfi->filename))
2925     return;
2926   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2927                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2928   if (fileExists(lfi->filename))
2929     return;
2930
2931   // check for Emerald Mine level file (V2 to V5)
2932   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2933   if (fileExists(lfi->filename))
2934     return;
2935
2936   // check for Emerald Mine level file (V6 / single mode)
2937   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2938   if (fileExists(lfi->filename))
2939     return;
2940   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2941   if (fileExists(lfi->filename))
2942     return;
2943
2944   // check for Emerald Mine level file (V6 / teamwork mode)
2945   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2946   if (fileExists(lfi->filename))
2947     return;
2948   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2949   if (fileExists(lfi->filename))
2950     return;
2951
2952   // check for various packed level file formats
2953   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2954   if (fileExists(lfi->filename))
2955     return;
2956
2957   // no known level file found -- use default values (and fail later)
2958   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2959                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2960 }
2961
2962 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2963 {
2964   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2965     lfi->type = getFileTypeFromBasename(lfi->basename);
2966
2967   if (lfi->type == LEVEL_FILE_TYPE_RND)
2968     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2969 }
2970
2971 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2972 {
2973   // always start with reliable default values
2974   setFileInfoToDefaults(level_file_info);
2975
2976   level_file_info->nr = nr;     // set requested level number
2977
2978   determineLevelFileInfo_Filename(level_file_info);
2979   determineLevelFileInfo_Filetype(level_file_info);
2980 }
2981
2982 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2983                               struct LevelFileInfo *lfi_to)
2984 {
2985   lfi_to->nr     = lfi_from->nr;
2986   lfi_to->type   = lfi_from->type;
2987   lfi_to->packed = lfi_from->packed;
2988
2989   setString(&lfi_to->basename, lfi_from->basename);
2990   setString(&lfi_to->filename, lfi_from->filename);
2991 }
2992
2993 // ----------------------------------------------------------------------------
2994 // functions for loading R'n'D level
2995 // ----------------------------------------------------------------------------
2996
2997 int getMappedElement(int element)
2998 {
2999   // remap some (historic, now obsolete) elements
3000
3001   switch (element)
3002   {
3003     case EL_PLAYER_OBSOLETE:
3004       element = EL_PLAYER_1;
3005       break;
3006
3007     case EL_KEY_OBSOLETE:
3008       element = EL_KEY_1;
3009       break;
3010
3011     case EL_EM_KEY_1_FILE_OBSOLETE:
3012       element = EL_EM_KEY_1;
3013       break;
3014
3015     case EL_EM_KEY_2_FILE_OBSOLETE:
3016       element = EL_EM_KEY_2;
3017       break;
3018
3019     case EL_EM_KEY_3_FILE_OBSOLETE:
3020       element = EL_EM_KEY_3;
3021       break;
3022
3023     case EL_EM_KEY_4_FILE_OBSOLETE:
3024       element = EL_EM_KEY_4;
3025       break;
3026
3027     case EL_ENVELOPE_OBSOLETE:
3028       element = EL_ENVELOPE_1;
3029       break;
3030
3031     case EL_SP_EMPTY:
3032       element = EL_EMPTY;
3033       break;
3034
3035     default:
3036       if (element >= NUM_FILE_ELEMENTS)
3037       {
3038         Warn("invalid level element %d", element);
3039
3040         element = EL_UNKNOWN;
3041       }
3042       break;
3043   }
3044
3045   return element;
3046 }
3047
3048 static int getMappedElementByVersion(int element, int game_version)
3049 {
3050   // remap some elements due to certain game version
3051
3052   if (game_version <= VERSION_IDENT(2,2,0,0))
3053   {
3054     // map game font elements
3055     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3056                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3057                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3058                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3059   }
3060
3061   if (game_version < VERSION_IDENT(3,0,0,0))
3062   {
3063     // map Supaplex gravity tube elements
3064     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3065                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3066                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3067                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3068                element);
3069   }
3070
3071   return element;
3072 }
3073
3074 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3075 {
3076   level->file_version = getFileVersion(file);
3077   level->game_version = getFileVersion(file);
3078
3079   return chunk_size;
3080 }
3081
3082 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3083 {
3084   level->creation_date.year  = getFile16BitBE(file);
3085   level->creation_date.month = getFile8Bit(file);
3086   level->creation_date.day   = getFile8Bit(file);
3087
3088   level->creation_date.src   = DATE_SRC_LEVELFILE;
3089
3090   return chunk_size;
3091 }
3092
3093 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3094 {
3095   int initial_player_stepsize;
3096   int initial_player_gravity;
3097   int i, x, y;
3098
3099   level->fieldx = getFile8Bit(file);
3100   level->fieldy = getFile8Bit(file);
3101
3102   level->time           = getFile16BitBE(file);
3103   level->gems_needed    = getFile16BitBE(file);
3104
3105   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3106     level->name[i] = getFile8Bit(file);
3107   level->name[MAX_LEVEL_NAME_LEN] = 0;
3108
3109   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3110     level->score[i] = getFile8Bit(file);
3111
3112   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3113   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3114     for (y = 0; y < 3; y++)
3115       for (x = 0; x < 3; x++)
3116         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3117
3118   level->amoeba_speed           = getFile8Bit(file);
3119   level->time_magic_wall        = getFile8Bit(file);
3120   level->time_wheel             = getFile8Bit(file);
3121   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3122
3123   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3124                                    STEPSIZE_NORMAL);
3125
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     level->initial_player_stepsize[i] = initial_player_stepsize;
3128
3129   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130
3131   for (i = 0; i < MAX_PLAYERS; i++)
3132     level->initial_player_gravity[i] = initial_player_gravity;
3133
3134   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136
3137   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3138
3139   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3141   level->can_move_into_acid_bits = getFile32BitBE(file);
3142   level->dont_collide_with_bits = getFile8Bit(file);
3143
3144   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3145   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3146
3147   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3148   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3149   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3150
3151   level->game_engine_type       = getFile8Bit(file);
3152
3153   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3154
3155   return chunk_size;
3156 }
3157
3158 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3159 {
3160   int i;
3161
3162   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3163     level->name[i] = getFile8Bit(file);
3164   level->name[MAX_LEVEL_NAME_LEN] = 0;
3165
3166   return chunk_size;
3167 }
3168
3169 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3170 {
3171   int i;
3172
3173   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3174     level->author[i] = getFile8Bit(file);
3175   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3176
3177   return chunk_size;
3178 }
3179
3180 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3181 {
3182   int x, y;
3183   int chunk_size_expected = level->fieldx * level->fieldy;
3184
3185   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3186      stored with 16-bit encoding (and should be twice as big then).
3187      Even worse, playfield data was stored 16-bit when only yamyam content
3188      contained 16-bit elements and vice versa. */
3189
3190   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3191     chunk_size_expected *= 2;
3192
3193   if (chunk_size_expected != chunk_size)
3194   {
3195     ReadUnusedBytesFromFile(file, chunk_size);
3196     return chunk_size_expected;
3197   }
3198
3199   for (y = 0; y < level->fieldy; y++)
3200     for (x = 0; x < level->fieldx; x++)
3201       level->field[x][y] =
3202         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3203                          getFile8Bit(file));
3204   return chunk_size;
3205 }
3206
3207 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3208 {
3209   int i, x, y;
3210   int header_size = 4;
3211   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3212   int chunk_size_expected = header_size + content_size;
3213
3214   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3215      stored with 16-bit encoding (and should be twice as big then).
3216      Even worse, playfield data was stored 16-bit when only yamyam content
3217      contained 16-bit elements and vice versa. */
3218
3219   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3220     chunk_size_expected += content_size;
3221
3222   if (chunk_size_expected != chunk_size)
3223   {
3224     ReadUnusedBytesFromFile(file, chunk_size);
3225     return chunk_size_expected;
3226   }
3227
3228   getFile8Bit(file);
3229   level->num_yamyam_contents = getFile8Bit(file);
3230   getFile8Bit(file);
3231   getFile8Bit(file);
3232
3233   // correct invalid number of content fields -- should never happen
3234   if (level->num_yamyam_contents < 1 ||
3235       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3236     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3237
3238   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3239     for (y = 0; y < 3; y++)
3240       for (x = 0; x < 3; x++)
3241         level->yamyam_content[i].e[x][y] =
3242           getMappedElement(level->encoding_16bit_field ?
3243                            getFile16BitBE(file) : getFile8Bit(file));
3244   return chunk_size;
3245 }
3246
3247 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3248 {
3249   int i, x, y;
3250   int element;
3251   int num_contents;
3252   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3253
3254   element = getMappedElement(getFile16BitBE(file));
3255   num_contents = getFile8Bit(file);
3256
3257   getFile8Bit(file);    // content x size (unused)
3258   getFile8Bit(file);    // content y size (unused)
3259
3260   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3261
3262   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3263     for (y = 0; y < 3; y++)
3264       for (x = 0; x < 3; x++)
3265         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3266
3267   // correct invalid number of content fields -- should never happen
3268   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3269     num_contents = STD_ELEMENT_CONTENTS;
3270
3271   if (element == EL_YAMYAM)
3272   {
3273     level->num_yamyam_contents = num_contents;
3274
3275     for (i = 0; i < num_contents; i++)
3276       for (y = 0; y < 3; y++)
3277         for (x = 0; x < 3; x++)
3278           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3279   }
3280   else if (element == EL_BD_AMOEBA)
3281   {
3282     level->amoeba_content = content_array[0][0][0];
3283   }
3284   else
3285   {
3286     Warn("cannot load content for element '%d'", element);
3287   }
3288
3289   return chunk_size;
3290 }
3291
3292 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3293 {
3294   int i;
3295   int element;
3296   int envelope_nr;
3297   int envelope_len;
3298   int chunk_size_expected;
3299
3300   element = getMappedElement(getFile16BitBE(file));
3301   if (!IS_ENVELOPE(element))
3302     element = EL_ENVELOPE_1;
3303
3304   envelope_nr = element - EL_ENVELOPE_1;
3305
3306   envelope_len = getFile16BitBE(file);
3307
3308   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3309   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3310
3311   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3312
3313   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3314   if (chunk_size_expected != chunk_size)
3315   {
3316     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3317     return chunk_size_expected;
3318   }
3319
3320   for (i = 0; i < envelope_len; i++)
3321     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3322
3323   return chunk_size;
3324 }
3325
3326 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3327 {
3328   int num_changed_custom_elements = getFile16BitBE(file);
3329   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3330   int i;
3331
3332   if (chunk_size_expected != chunk_size)
3333   {
3334     ReadUnusedBytesFromFile(file, chunk_size - 2);
3335     return chunk_size_expected;
3336   }
3337
3338   for (i = 0; i < num_changed_custom_elements; i++)
3339   {
3340     int element = getMappedElement(getFile16BitBE(file));
3341     int properties = getFile32BitBE(file);
3342
3343     if (IS_CUSTOM_ELEMENT(element))
3344       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3345     else
3346       Warn("invalid custom element number %d", element);
3347
3348     // older game versions that wrote level files with CUS1 chunks used
3349     // different default push delay values (not yet stored in level file)
3350     element_info[element].push_delay_fixed = 2;
3351     element_info[element].push_delay_random = 8;
3352   }
3353
3354   level->file_has_custom_elements = TRUE;
3355
3356   return chunk_size;
3357 }
3358
3359 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3360 {
3361   int num_changed_custom_elements = getFile16BitBE(file);
3362   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3363   int i;
3364
3365   if (chunk_size_expected != chunk_size)
3366   {
3367     ReadUnusedBytesFromFile(file, chunk_size - 2);
3368     return chunk_size_expected;
3369   }
3370
3371   for (i = 0; i < num_changed_custom_elements; i++)
3372   {
3373     int element = getMappedElement(getFile16BitBE(file));
3374     int custom_target_element = getMappedElement(getFile16BitBE(file));
3375
3376     if (IS_CUSTOM_ELEMENT(element))
3377       element_info[element].change->target_element = custom_target_element;
3378     else
3379       Warn("invalid custom element number %d", element);
3380   }
3381
3382   level->file_has_custom_elements = TRUE;
3383
3384   return chunk_size;
3385 }
3386
3387 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3388 {
3389   int num_changed_custom_elements = getFile16BitBE(file);
3390   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3391   int i, j, x, y;
3392
3393   if (chunk_size_expected != chunk_size)
3394   {
3395     ReadUnusedBytesFromFile(file, chunk_size - 2);
3396     return chunk_size_expected;
3397   }
3398
3399   for (i = 0; i < num_changed_custom_elements; i++)
3400   {
3401     int element = getMappedElement(getFile16BitBE(file));
3402     struct ElementInfo *ei = &element_info[element];
3403     unsigned int event_bits;
3404
3405     if (!IS_CUSTOM_ELEMENT(element))
3406     {
3407       Warn("invalid custom element number %d", element);
3408
3409       element = EL_INTERNAL_DUMMY;
3410     }
3411
3412     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3413       ei->description[j] = getFile8Bit(file);
3414     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3415
3416     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3417
3418     // some free bytes for future properties and padding
3419     ReadUnusedBytesFromFile(file, 7);
3420
3421     ei->use_gfx_element = getFile8Bit(file);
3422     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3423
3424     ei->collect_score_initial = getFile8Bit(file);
3425     ei->collect_count_initial = getFile8Bit(file);
3426
3427     ei->push_delay_fixed = getFile16BitBE(file);
3428     ei->push_delay_random = getFile16BitBE(file);
3429     ei->move_delay_fixed = getFile16BitBE(file);
3430     ei->move_delay_random = getFile16BitBE(file);
3431
3432     ei->move_pattern = getFile16BitBE(file);
3433     ei->move_direction_initial = getFile8Bit(file);
3434     ei->move_stepsize = getFile8Bit(file);
3435
3436     for (y = 0; y < 3; y++)
3437       for (x = 0; x < 3; x++)
3438         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3439
3440     // bits 0 - 31 of "has_event[]"
3441     event_bits = getFile32BitBE(file);
3442     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3443       if (event_bits & (1u << j))
3444         ei->change->has_event[j] = TRUE;
3445
3446     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3447
3448     ei->change->delay_fixed = getFile16BitBE(file);
3449     ei->change->delay_random = getFile16BitBE(file);
3450     ei->change->delay_frames = getFile16BitBE(file);
3451
3452     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3453
3454     ei->change->explode = getFile8Bit(file);
3455     ei->change->use_target_content = getFile8Bit(file);
3456     ei->change->only_if_complete = getFile8Bit(file);
3457     ei->change->use_random_replace = getFile8Bit(file);
3458
3459     ei->change->random_percentage = getFile8Bit(file);
3460     ei->change->replace_when = getFile8Bit(file);
3461
3462     for (y = 0; y < 3; y++)
3463       for (x = 0; x < 3; x++)
3464         ei->change->target_content.e[x][y] =
3465           getMappedElement(getFile16BitBE(file));
3466
3467     ei->slippery_type = getFile8Bit(file);
3468
3469     // some free bytes for future properties and padding
3470     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3471
3472     // mark that this custom element has been modified
3473     ei->modified_settings = TRUE;
3474   }
3475
3476   level->file_has_custom_elements = TRUE;
3477
3478   return chunk_size;
3479 }
3480
3481 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3482 {
3483   struct ElementInfo *ei;
3484   int chunk_size_expected;
3485   int element;
3486   int i, j, x, y;
3487
3488   // ---------- custom element base property values (96 bytes) ----------------
3489
3490   element = getMappedElement(getFile16BitBE(file));
3491
3492   if (!IS_CUSTOM_ELEMENT(element))
3493   {
3494     Warn("invalid custom element number %d", element);
3495
3496     ReadUnusedBytesFromFile(file, chunk_size - 2);
3497
3498     return chunk_size;
3499   }
3500
3501   ei = &element_info[element];
3502
3503   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3504     ei->description[i] = getFile8Bit(file);
3505   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3506
3507   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3508
3509   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3510
3511   ei->num_change_pages = getFile8Bit(file);
3512
3513   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3514   if (chunk_size_expected != chunk_size)
3515   {
3516     ReadUnusedBytesFromFile(file, chunk_size - 43);
3517     return chunk_size_expected;
3518   }
3519
3520   ei->ce_value_fixed_initial = getFile16BitBE(file);
3521   ei->ce_value_random_initial = getFile16BitBE(file);
3522   ei->use_last_ce_value = getFile8Bit(file);
3523
3524   ei->use_gfx_element = getFile8Bit(file);
3525   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3526
3527   ei->collect_score_initial = getFile8Bit(file);
3528   ei->collect_count_initial = getFile8Bit(file);
3529
3530   ei->drop_delay_fixed = getFile8Bit(file);
3531   ei->push_delay_fixed = getFile8Bit(file);
3532   ei->drop_delay_random = getFile8Bit(file);
3533   ei->push_delay_random = getFile8Bit(file);
3534   ei->move_delay_fixed = getFile16BitBE(file);
3535   ei->move_delay_random = getFile16BitBE(file);
3536
3537   // bits 0 - 15 of "move_pattern" ...
3538   ei->move_pattern = getFile16BitBE(file);
3539   ei->move_direction_initial = getFile8Bit(file);
3540   ei->move_stepsize = getFile8Bit(file);
3541
3542   ei->slippery_type = getFile8Bit(file);
3543
3544   for (y = 0; y < 3; y++)
3545     for (x = 0; x < 3; x++)
3546       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3547
3548   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3549   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3550   ei->move_leave_type = getFile8Bit(file);
3551
3552   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3553   ei->move_pattern |= (getFile16BitBE(file) << 16);
3554
3555   ei->access_direction = getFile8Bit(file);
3556
3557   ei->explosion_delay = getFile8Bit(file);
3558   ei->ignition_delay = getFile8Bit(file);
3559   ei->explosion_type = getFile8Bit(file);
3560
3561   // some free bytes for future custom property values and padding
3562   ReadUnusedBytesFromFile(file, 1);
3563
3564   // ---------- change page property values (48 bytes) ------------------------
3565
3566   setElementChangePages(ei, ei->num_change_pages);
3567
3568   for (i = 0; i < ei->num_change_pages; i++)
3569   {
3570     struct ElementChangeInfo *change = &ei->change_page[i];
3571     unsigned int event_bits;
3572
3573     // always start with reliable default values
3574     setElementChangeInfoToDefaults(change);
3575
3576     // bits 0 - 31 of "has_event[]" ...
3577     event_bits = getFile32BitBE(file);
3578     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3579       if (event_bits & (1u << j))
3580         change->has_event[j] = TRUE;
3581
3582     change->target_element = getMappedElement(getFile16BitBE(file));
3583
3584     change->delay_fixed = getFile16BitBE(file);
3585     change->delay_random = getFile16BitBE(file);
3586     change->delay_frames = getFile16BitBE(file);
3587
3588     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3589
3590     change->explode = getFile8Bit(file);
3591     change->use_target_content = getFile8Bit(file);
3592     change->only_if_complete = getFile8Bit(file);
3593     change->use_random_replace = getFile8Bit(file);
3594
3595     change->random_percentage = getFile8Bit(file);
3596     change->replace_when = getFile8Bit(file);
3597
3598     for (y = 0; y < 3; y++)
3599       for (x = 0; x < 3; x++)
3600         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3601
3602     change->can_change = getFile8Bit(file);
3603
3604     change->trigger_side = getFile8Bit(file);
3605
3606     change->trigger_player = getFile8Bit(file);
3607     change->trigger_page = getFile8Bit(file);
3608
3609     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3610                             CH_PAGE_ANY : (1 << change->trigger_page));
3611
3612     change->has_action = getFile8Bit(file);
3613     change->action_type = getFile8Bit(file);
3614     change->action_mode = getFile8Bit(file);
3615     change->action_arg = getFile16BitBE(file);
3616
3617     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3618     event_bits = getFile8Bit(file);
3619     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3620       if (event_bits & (1u << (j - 32)))
3621         change->has_event[j] = TRUE;
3622   }
3623
3624   // mark this custom element as modified
3625   ei->modified_settings = TRUE;
3626
3627   level->file_has_custom_elements = TRUE;
3628
3629   return chunk_size;
3630 }
3631
3632 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3633 {
3634   struct ElementInfo *ei;
3635   struct ElementGroupInfo *group;
3636   int element;
3637   int i;
3638
3639   element = getMappedElement(getFile16BitBE(file));
3640
3641   if (!IS_GROUP_ELEMENT(element))
3642   {
3643     Warn("invalid group element number %d", element);
3644
3645     ReadUnusedBytesFromFile(file, chunk_size - 2);
3646
3647     return chunk_size;
3648   }
3649
3650   ei = &element_info[element];
3651
3652   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3653     ei->description[i] = getFile8Bit(file);
3654   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3655
3656   group = element_info[element].group;
3657
3658   group->num_elements = getFile8Bit(file);
3659
3660   ei->use_gfx_element = getFile8Bit(file);
3661   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3662
3663   group->choice_mode = getFile8Bit(file);
3664
3665   // some free bytes for future values and padding
3666   ReadUnusedBytesFromFile(file, 3);
3667
3668   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3669     group->element[i] = getMappedElement(getFile16BitBE(file));
3670
3671   // mark this group element as modified
3672   element_info[element].modified_settings = TRUE;
3673
3674   level->file_has_custom_elements = TRUE;
3675
3676   return chunk_size;
3677 }
3678
3679 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3680                                 int element, int real_element)
3681 {
3682   int micro_chunk_size = 0;
3683   int conf_type = getFile8Bit(file);
3684   int byte_mask = conf_type & CONF_MASK_BYTES;
3685   boolean element_found = FALSE;
3686   int i;
3687
3688   micro_chunk_size += 1;
3689
3690   if (byte_mask == CONF_MASK_MULTI_BYTES)
3691   {
3692     int num_bytes = getFile16BitBE(file);
3693     byte *buffer = checked_malloc(num_bytes);
3694
3695     ReadBytesFromFile(file, buffer, num_bytes);
3696
3697     for (i = 0; conf[i].data_type != -1; i++)
3698     {
3699       if (conf[i].element == element &&
3700           conf[i].conf_type == conf_type)
3701       {
3702         int data_type = conf[i].data_type;
3703         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3704         int max_num_entities = conf[i].max_num_entities;
3705
3706         if (num_entities > max_num_entities)
3707         {
3708           Warn("truncating number of entities for element %d from %d to %d",
3709                element, num_entities, max_num_entities);
3710
3711           num_entities = max_num_entities;
3712         }
3713
3714         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3715                                   data_type == TYPE_CONTENT_LIST))
3716         {
3717           // for element and content lists, zero entities are not allowed
3718           Warn("found empty list of entities for element %d", element);
3719
3720           // do not set "num_entities" here to prevent reading behind buffer
3721
3722           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3723         }
3724         else
3725         {
3726           *(int *)(conf[i].num_entities) = num_entities;
3727         }
3728
3729         element_found = TRUE;
3730
3731         if (data_type == TYPE_STRING)
3732         {
3733           char *string = (char *)(conf[i].value);
3734           int j;
3735
3736           for (j = 0; j < max_num_entities; j++)
3737             string[j] = (j < num_entities ? buffer[j] : '\0');
3738         }
3739         else if (data_type == TYPE_ELEMENT_LIST)
3740         {
3741           int *element_array = (int *)(conf[i].value);
3742           int j;
3743
3744           for (j = 0; j < num_entities; j++)
3745             element_array[j] =
3746               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3747         }
3748         else if (data_type == TYPE_CONTENT_LIST)
3749         {
3750           struct Content *content= (struct Content *)(conf[i].value);
3751           int c, x, y;
3752
3753           for (c = 0; c < num_entities; c++)
3754             for (y = 0; y < 3; y++)
3755               for (x = 0; x < 3; x++)
3756                 content[c].e[x][y] =
3757                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3758         }
3759         else
3760           element_found = FALSE;
3761
3762         break;
3763       }
3764     }
3765
3766     checked_free(buffer);
3767
3768     micro_chunk_size += 2 + num_bytes;
3769   }
3770   else          // constant size configuration data (1, 2 or 4 bytes)
3771   {
3772     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3773                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3774                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3775
3776     for (i = 0; conf[i].data_type != -1; i++)
3777     {
3778       if (conf[i].element == element &&
3779           conf[i].conf_type == conf_type)
3780       {
3781         int data_type = conf[i].data_type;
3782
3783         if (data_type == TYPE_ELEMENT)
3784           value = getMappedElement(value);
3785
3786         if (data_type == TYPE_BOOLEAN)
3787           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3788         else
3789           *(int *)    (conf[i].value) = value;
3790
3791         element_found = TRUE;
3792
3793         break;
3794       }
3795     }
3796
3797     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3798   }
3799
3800   if (!element_found)
3801   {
3802     char *error_conf_chunk_bytes =
3803       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3804        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3805        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3806     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3807     int error_element = real_element;
3808
3809     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3810          error_conf_chunk_bytes, error_conf_chunk_token,
3811          error_element, EL_NAME(error_element));
3812   }
3813
3814   return micro_chunk_size;
3815 }
3816
3817 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3818 {
3819   int real_chunk_size = 0;
3820
3821   li = *level;          // copy level data into temporary buffer
3822
3823   while (!checkEndOfFile(file))
3824   {
3825     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3826
3827     if (real_chunk_size >= chunk_size)
3828       break;
3829   }
3830
3831   *level = li;          // copy temporary buffer back to level data
3832
3833   return real_chunk_size;
3834 }
3835
3836 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3837 {
3838   int real_chunk_size = 0;
3839
3840   li = *level;          // copy level data into temporary buffer
3841
3842   while (!checkEndOfFile(file))
3843   {
3844     int element = getMappedElement(getFile16BitBE(file));
3845
3846     real_chunk_size += 2;
3847     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3848                                             element, element);
3849     if (real_chunk_size >= chunk_size)
3850       break;
3851   }
3852
3853   *level = li;          // copy temporary buffer back to level data
3854
3855   return real_chunk_size;
3856 }
3857
3858 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3859 {
3860   int real_chunk_size = 0;
3861
3862   li = *level;          // copy level data into temporary buffer
3863
3864   while (!checkEndOfFile(file))
3865   {
3866     int element = getMappedElement(getFile16BitBE(file));
3867
3868     real_chunk_size += 2;
3869     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3870                                             element, element);
3871     if (real_chunk_size >= chunk_size)
3872       break;
3873   }
3874
3875   *level = li;          // copy temporary buffer back to level data
3876
3877   return real_chunk_size;
3878 }
3879
3880 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3881 {
3882   int element = getMappedElement(getFile16BitBE(file));
3883   int envelope_nr = element - EL_ENVELOPE_1;
3884   int real_chunk_size = 2;
3885
3886   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3887
3888   while (!checkEndOfFile(file))
3889   {
3890     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3891                                             -1, element);
3892
3893     if (real_chunk_size >= chunk_size)
3894       break;
3895   }
3896
3897   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3898
3899   return real_chunk_size;
3900 }
3901
3902 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int element = getMappedElement(getFile16BitBE(file));
3905   int real_chunk_size = 2;
3906   struct ElementInfo *ei = &element_info[element];
3907   int i;
3908
3909   xx_ei = *ei;          // copy element data into temporary buffer
3910
3911   xx_ei.num_change_pages = -1;
3912
3913   while (!checkEndOfFile(file))
3914   {
3915     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3916                                             -1, element);
3917     if (xx_ei.num_change_pages != -1)
3918       break;
3919
3920     if (real_chunk_size >= chunk_size)
3921       break;
3922   }
3923
3924   *ei = xx_ei;
3925
3926   if (ei->num_change_pages == -1)
3927   {
3928     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3929          EL_NAME(element));
3930
3931     ei->num_change_pages = 1;
3932
3933     setElementChangePages(ei, 1);
3934     setElementChangeInfoToDefaults(ei->change);
3935
3936     return real_chunk_size;
3937   }
3938
3939   // initialize number of change pages stored for this custom element
3940   setElementChangePages(ei, ei->num_change_pages);
3941   for (i = 0; i < ei->num_change_pages; i++)
3942     setElementChangeInfoToDefaults(&ei->change_page[i]);
3943
3944   // start with reading properties for the first change page
3945   xx_current_change_page = 0;
3946
3947   while (!checkEndOfFile(file))
3948   {
3949     // level file might contain invalid change page number
3950     if (xx_current_change_page >= ei->num_change_pages)
3951       break;
3952
3953     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3954
3955     xx_change = *change;        // copy change data into temporary buffer
3956
3957     resetEventBits();           // reset bits; change page might have changed
3958
3959     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3960                                             -1, element);
3961
3962     *change = xx_change;
3963
3964     setEventFlagsFromEventBits(change);
3965
3966     if (real_chunk_size >= chunk_size)
3967       break;
3968   }
3969
3970   level->file_has_custom_elements = TRUE;
3971
3972   return real_chunk_size;
3973 }
3974
3975 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3976 {
3977   int element = getMappedElement(getFile16BitBE(file));
3978   int real_chunk_size = 2;
3979   struct ElementInfo *ei = &element_info[element];
3980   struct ElementGroupInfo *group = ei->group;
3981
3982   if (group == NULL)
3983     return -1;
3984
3985   xx_ei = *ei;          // copy element data into temporary buffer
3986   xx_group = *group;    // copy group data into temporary buffer
3987
3988   while (!checkEndOfFile(file))
3989   {
3990     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3991                                             -1, element);
3992
3993     if (real_chunk_size >= chunk_size)
3994       break;
3995   }
3996
3997   *ei = xx_ei;
3998   *group = xx_group;
3999
4000   level->file_has_custom_elements = TRUE;
4001
4002   return real_chunk_size;
4003 }
4004
4005 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4006 {
4007   int element = getMappedElement(getFile16BitBE(file));
4008   int real_chunk_size = 2;
4009   struct ElementInfo *ei = &element_info[element];
4010
4011   xx_ei = *ei;          // copy element data into temporary buffer
4012
4013   while (!checkEndOfFile(file))
4014   {
4015     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4016                                             -1, element);
4017
4018     if (real_chunk_size >= chunk_size)
4019       break;
4020   }
4021
4022   *ei = xx_ei;
4023
4024   level->file_has_custom_elements = TRUE;
4025
4026   return real_chunk_size;
4027 }
4028
4029 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4030                                       struct LevelFileInfo *level_file_info,
4031                                       boolean level_info_only)
4032 {
4033   char *filename = level_file_info->filename;
4034   char cookie[MAX_LINE_LEN];
4035   char chunk_name[CHUNK_ID_LEN + 1];
4036   int chunk_size;
4037   File *file;
4038
4039   if (!(file = openFile(filename, MODE_READ)))
4040   {
4041     level->no_valid_file = TRUE;
4042     level->no_level_file = TRUE;
4043
4044     if (level_info_only)
4045       return;
4046
4047     Warn("cannot read level '%s' -- using empty level", filename);
4048
4049     if (!setup.editor.use_template_for_new_levels)
4050       return;
4051
4052     // if level file not found, try to initialize level data from template
4053     filename = getGlobalLevelTemplateFilename();
4054
4055     if (!(file = openFile(filename, MODE_READ)))
4056       return;
4057
4058     // default: for empty levels, use level template for custom elements
4059     level->use_custom_template = TRUE;
4060
4061     level->no_valid_file = FALSE;
4062   }
4063
4064   getFileChunkBE(file, chunk_name, NULL);
4065   if (strEqual(chunk_name, "RND1"))
4066   {
4067     getFile32BitBE(file);               // not used
4068
4069     getFileChunkBE(file, chunk_name, NULL);
4070     if (!strEqual(chunk_name, "CAVE"))
4071     {
4072       level->no_valid_file = TRUE;
4073
4074       Warn("unknown format of level file '%s'", filename);
4075
4076       closeFile(file);
4077
4078       return;
4079     }
4080   }
4081   else  // check for pre-2.0 file format with cookie string
4082   {
4083     strcpy(cookie, chunk_name);
4084     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4085       cookie[4] = '\0';
4086     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4087       cookie[strlen(cookie) - 1] = '\0';
4088
4089     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4090     {
4091       level->no_valid_file = TRUE;
4092
4093       Warn("unknown format of level file '%s'", filename);
4094
4095       closeFile(file);
4096
4097       return;
4098     }
4099
4100     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4101     {
4102       level->no_valid_file = TRUE;
4103
4104       Warn("unsupported version of level file '%s'", filename);
4105
4106       closeFile(file);
4107
4108       return;
4109     }
4110
4111     // pre-2.0 level files have no game version, so use file version here
4112     level->game_version = level->file_version;
4113   }
4114
4115   if (level->file_version < FILE_VERSION_1_2)
4116   {
4117     // level files from versions before 1.2.0 without chunk structure
4118     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4119     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4120   }
4121   else
4122   {
4123     static struct
4124     {
4125       char *name;
4126       int size;
4127       int (*loader)(File *, int, struct LevelInfo *);
4128     }
4129     chunk_info[] =
4130     {
4131       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4132       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4133       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4134       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4135       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4136       { "INFO", -1,                     LoadLevel_INFO },
4137       { "BODY", -1,                     LoadLevel_BODY },
4138       { "CONT", -1,                     LoadLevel_CONT },
4139       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4140       { "CNT3", -1,                     LoadLevel_CNT3 },
4141       { "CUS1", -1,                     LoadLevel_CUS1 },
4142       { "CUS2", -1,                     LoadLevel_CUS2 },
4143       { "CUS3", -1,                     LoadLevel_CUS3 },
4144       { "CUS4", -1,                     LoadLevel_CUS4 },
4145       { "GRP1", -1,                     LoadLevel_GRP1 },
4146       { "CONF", -1,                     LoadLevel_CONF },
4147       { "ELEM", -1,                     LoadLevel_ELEM },
4148       { "NOTE", -1,                     LoadLevel_NOTE },
4149       { "CUSX", -1,                     LoadLevel_CUSX },
4150       { "GRPX", -1,                     LoadLevel_GRPX },
4151       { "EMPX", -1,                     LoadLevel_EMPX },
4152
4153       {  NULL,  0,                      NULL }
4154     };
4155
4156     while (getFileChunkBE(file, chunk_name, &chunk_size))
4157     {
4158       int i = 0;
4159
4160       while (chunk_info[i].name != NULL &&
4161              !strEqual(chunk_name, chunk_info[i].name))
4162         i++;
4163
4164       if (chunk_info[i].name == NULL)
4165       {
4166         Warn("unknown chunk '%s' in level file '%s'",
4167              chunk_name, filename);
4168
4169         ReadUnusedBytesFromFile(file, chunk_size);
4170       }
4171       else if (chunk_info[i].size != -1 &&
4172                chunk_info[i].size != chunk_size)
4173       {
4174         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4175              chunk_size, chunk_name, filename);
4176
4177         ReadUnusedBytesFromFile(file, chunk_size);
4178       }
4179       else
4180       {
4181         // call function to load this level chunk
4182         int chunk_size_expected =
4183           (chunk_info[i].loader)(file, chunk_size, level);
4184
4185         if (chunk_size_expected < 0)
4186         {
4187           Warn("error reading chunk '%s' in level file '%s'",
4188                chunk_name, filename);
4189
4190           break;
4191         }
4192
4193         // the size of some chunks cannot be checked before reading other
4194         // chunks first (like "HEAD" and "BODY") that contain some header
4195         // information, so check them here
4196         if (chunk_size_expected != chunk_size)
4197         {
4198           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4199                chunk_size, chunk_name, filename);
4200
4201           break;
4202         }
4203       }
4204     }
4205   }
4206
4207   closeFile(file);
4208 }
4209
4210
4211 // ----------------------------------------------------------------------------
4212 // functions for loading BD level
4213 // ----------------------------------------------------------------------------
4214
4215 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4216 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4217
4218 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4219 {
4220   struct LevelInfo_BD *level_bd = level->native_bd_level;
4221   GdCave *cave = NULL;  // will be changed below
4222   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4223   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4224   int x, y;
4225
4226   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4227
4228   // cave and map newly allocated when set to defaults above
4229   cave = level_bd->cave;
4230
4231   // level type
4232   cave->intermission                    = level->bd_intermission;
4233
4234   // level settings
4235   cave->level_time[0]                   = level->time;
4236   cave->level_diamonds[0]               = level->gems_needed;
4237
4238   // game timing
4239   cave->scheduling                      = level->bd_scheduling_type;
4240   cave->pal_timing                      = level->bd_pal_timing;
4241   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4242   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4243   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4244   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4245
4246   // scores
4247   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4248   cave->diamond_value                   = level->score[SC_EMERALD];
4249   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4250
4251   // compatibility settings
4252   cave->lineshift                       = level->bd_line_shifting_borders;
4253   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4254   cave->short_explosions                = level->bd_short_explosions;
4255
4256   // player properties
4257   cave->diagonal_movements              = level->bd_diagonal_movements;
4258   cave->active_is_first_found           = level->bd_topmost_player_active;
4259   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4260   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4261   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4262   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4263
4264   // element properties
4265   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4266   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4267   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4268   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4269   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4270   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4271   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4272   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4273   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4274   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4275
4276   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4277   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4278   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4279   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4280   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4281   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4282   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4283
4284   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4285   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4286   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4287   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4288   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4289   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4290   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4291   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4292   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4293   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4294   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4295
4296   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4297   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4298   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4299   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4300   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4301   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4302
4303   cave->slime_predictable               = level->bd_slime_is_predictable;
4304   cave->slime_correct_random            = level->bd_slime_correct_random;
4305   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4306   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4307   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4308   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4309   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4310   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4311   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4312   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4313   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4314   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4315
4316   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4317   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4318   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4319
4320   cave->biter_delay_frame               = level->bd_biter_move_delay;
4321   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4322
4323   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4324
4325   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4326
4327   cave->replicators_active              = level->bd_replicators_active;
4328   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4329
4330   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4331   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4332
4333   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4334
4335   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4336
4337   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4338   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4339   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4340
4341   cave->infinite_rockets                = level->bd_infinite_rockets;
4342
4343   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4344   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4345
4346   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4347   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4348
4349   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4350   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4351   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4352
4353   cave->gravity                         = level->bd_gravity_direction;
4354   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4355   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4356   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4357
4358   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4359   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4360   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4361   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4362
4363   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4364   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4365   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4366   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4367   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4368   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4369
4370   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4371   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4372   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4373   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4374
4375   // level name
4376   strncpy(cave->name, level->name, sizeof(GdString));
4377   cave->name[sizeof(GdString) - 1] = '\0';
4378
4379   // playfield elements
4380   for (x = 0; x < cave->w; x++)
4381     for (y = 0; y < cave->h; y++)
4382       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4383 }
4384
4385 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4386 {
4387   struct LevelInfo_BD *level_bd = level->native_bd_level;
4388   GdCave *cave = level_bd->cave;
4389   int bd_level_nr = level_bd->level_nr;
4390   int x, y;
4391
4392   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4393   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4394
4395   // level type
4396   level->bd_intermission                = cave->intermission;
4397
4398   // level settings
4399   level->time                           = cave->level_time[bd_level_nr];
4400   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4401
4402   // game timing
4403   level->bd_scheduling_type             = cave->scheduling;
4404   level->bd_pal_timing                  = cave->pal_timing;
4405   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4406   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4407   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4408   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4409
4410   // scores
4411   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4412   level->score[SC_EMERALD]              = cave->diamond_value;
4413   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4414
4415   // compatibility settings
4416   level->bd_line_shifting_borders       = cave->lineshift;
4417   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4418   level->bd_short_explosions            = cave->short_explosions;
4419
4420   // player properties
4421   level->bd_diagonal_movements          = cave->diagonal_movements;
4422   level->bd_topmost_player_active       = cave->active_is_first_found;
4423   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4424   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4425   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4426   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4427
4428   // element properties
4429   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4430   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4431   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4432   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4433   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4434   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4435   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4436   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4437   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4438   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4439
4440   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4441   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4442   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4443   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4444   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4445   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4446   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4447
4448   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4449   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4450   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4451   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4452   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4453   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4454   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4455   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4456   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4457   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4458   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4459
4460   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4461   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4462   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4463   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4464   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4465   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4466
4467   level->bd_slime_is_predictable        = cave->slime_predictable;
4468   level->bd_slime_correct_random        = cave->slime_correct_random;
4469   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4470   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4471   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4472   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4473   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4474   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4475   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4476   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4477   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4478   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4479
4480   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4481   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4482   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4483
4484   level->bd_biter_move_delay            = cave->biter_delay_frame;
4485   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4486
4487   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4488
4489   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4490
4491   level->bd_replicators_active          = cave->replicators_active;
4492   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4493
4494   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4495   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4496
4497   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4498
4499   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4500
4501   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4502   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4503   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4504
4505   level->bd_infinite_rockets            = cave->infinite_rockets;
4506
4507   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4508   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4509
4510   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4511   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4512
4513   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4514   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4515   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4516
4517   level->bd_gravity_direction           = cave->gravity;
4518   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4519   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4520   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4521
4522   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4523   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4524   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4525   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4526
4527   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4528   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4529   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4530   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4531   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4532   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4533
4534   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4535   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4536   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4537   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4538
4539   // level name
4540   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4541
4542   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4543   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4544
4545   // playfield elements
4546   for (x = 0; x < level->fieldx; x++)
4547     for (y = 0; y < level->fieldy; y++)
4548       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4549
4550   checked_free(cave_name);
4551 }
4552
4553 static void setTapeInfoToDefaults(void);
4554
4555 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4556 {
4557   struct LevelInfo_BD *level_bd = level->native_bd_level;
4558   GdCave *cave = level_bd->cave;
4559   GdReplay *replay = level_bd->replay;
4560   int i;
4561
4562   if (replay == NULL)
4563     return;
4564
4565   // always start with reliable default values
4566   setTapeInfoToDefaults();
4567
4568   tape.level_nr = level_nr;             // (currently not used)
4569   tape.random_seed = replay->seed;
4570
4571   TapeSetDateFromIsoDateString(replay->date);
4572
4573   tape.counter = 0;
4574   tape.pos[tape.counter].delay = 0;
4575
4576   tape.bd_replay = TRUE;
4577
4578   // all time calculations only used to display approximate tape time
4579   int cave_speed = cave->speed;
4580   int milliseconds_game = 0;
4581   int milliseconds_elapsed = 20;
4582
4583   for (i = 0; i < replay->movements->len; i++)
4584   {
4585     int replay_action = replay->movements->data[i];
4586     int tape_action = map_action_BD_to_RND(replay_action);
4587     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4588     boolean success = 0;
4589
4590     while (1)
4591     {
4592       success = TapeAddAction(action);
4593
4594       milliseconds_game += milliseconds_elapsed;
4595
4596       if (milliseconds_game >= cave_speed)
4597       {
4598         milliseconds_game -= cave_speed;
4599
4600         break;
4601       }
4602     }
4603
4604     tape.counter++;
4605     tape.pos[tape.counter].delay = 0;
4606     tape.pos[tape.counter].action[0] = 0;
4607
4608     if (!success)
4609     {
4610       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4611
4612       break;
4613     }
4614   }
4615
4616   TapeHaltRecording();
4617
4618   if (!replay->success)
4619     Warn("BD replay is marked as not successful");
4620 }
4621
4622
4623 // ----------------------------------------------------------------------------
4624 // functions for loading EM level
4625 // ----------------------------------------------------------------------------
4626
4627 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4628 {
4629   static int ball_xy[8][2] =
4630   {
4631     { 0, 0 },
4632     { 1, 0 },
4633     { 2, 0 },
4634     { 0, 1 },
4635     { 2, 1 },
4636     { 0, 2 },
4637     { 1, 2 },
4638     { 2, 2 },
4639   };
4640   struct LevelInfo_EM *level_em = level->native_em_level;
4641   struct CAVE *cav = level_em->cav;
4642   int i, j, x, y;
4643
4644   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4645   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4646
4647   cav->time_seconds     = level->time;
4648   cav->gems_needed      = level->gems_needed;
4649
4650   cav->emerald_score    = level->score[SC_EMERALD];
4651   cav->diamond_score    = level->score[SC_DIAMOND];
4652   cav->alien_score      = level->score[SC_ROBOT];
4653   cav->tank_score       = level->score[SC_SPACESHIP];
4654   cav->bug_score        = level->score[SC_BUG];
4655   cav->eater_score      = level->score[SC_YAMYAM];
4656   cav->nut_score        = level->score[SC_NUT];
4657   cav->dynamite_score   = level->score[SC_DYNAMITE];
4658   cav->key_score        = level->score[SC_KEY];
4659   cav->exit_score       = level->score[SC_TIME_BONUS];
4660
4661   cav->num_eater_arrays = level->num_yamyam_contents;
4662
4663   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4664     for (y = 0; y < 3; y++)
4665       for (x = 0; x < 3; x++)
4666         cav->eater_array[i][y * 3 + x] =
4667           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4668
4669   cav->amoeba_time              = level->amoeba_speed;
4670   cav->wonderwall_time          = level->time_magic_wall;
4671   cav->wheel_time               = level->time_wheel;
4672
4673   cav->android_move_time        = level->android_move_time;
4674   cav->android_clone_time       = level->android_clone_time;
4675   cav->ball_random              = level->ball_random;
4676   cav->ball_active              = level->ball_active_initial;
4677   cav->ball_time                = level->ball_time;
4678   cav->num_ball_arrays          = level->num_ball_contents;
4679
4680   cav->lenses_score             = level->lenses_score;
4681   cav->magnify_score            = level->magnify_score;
4682   cav->slurp_score              = level->slurp_score;
4683
4684   cav->lenses_time              = level->lenses_time;
4685   cav->magnify_time             = level->magnify_time;
4686
4687   cav->wind_time = 9999;
4688   cav->wind_direction =
4689     map_direction_RND_to_EM(level->wind_direction_initial);
4690
4691   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4692     for (j = 0; j < 8; j++)
4693       cav->ball_array[i][j] =
4694         map_element_RND_to_EM_cave(level->ball_content[i].
4695                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4696
4697   map_android_clone_elements_RND_to_EM(level);
4698
4699   // first fill the complete playfield with the empty space element
4700   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4701     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4702       cav->cave[x][y] = Cblank;
4703
4704   // then copy the real level contents from level file into the playfield
4705   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4706   {
4707     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4708
4709     if (level->field[x][y] == EL_AMOEBA_DEAD)
4710       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4711
4712     cav->cave[x][y] = new_element;
4713   }
4714
4715   for (i = 0; i < MAX_PLAYERS; i++)
4716   {
4717     cav->player_x[i] = -1;
4718     cav->player_y[i] = -1;
4719   }
4720
4721   // initialize player positions and delete players from the playfield
4722   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4723   {
4724     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4725     {
4726       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4727
4728       cav->player_x[player_nr] = x;
4729       cav->player_y[player_nr] = y;
4730
4731       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4732     }
4733   }
4734 }
4735
4736 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4737 {
4738   static int ball_xy[8][2] =
4739   {
4740     { 0, 0 },
4741     { 1, 0 },
4742     { 2, 0 },
4743     { 0, 1 },
4744     { 2, 1 },
4745     { 0, 2 },
4746     { 1, 2 },
4747     { 2, 2 },
4748   };
4749   struct LevelInfo_EM *level_em = level->native_em_level;
4750   struct CAVE *cav = level_em->cav;
4751   int i, j, x, y;
4752
4753   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4754   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4755
4756   level->time        = cav->time_seconds;
4757   level->gems_needed = cav->gems_needed;
4758
4759   sprintf(level->name, "Level %d", level->file_info.nr);
4760
4761   level->score[SC_EMERALD]      = cav->emerald_score;
4762   level->score[SC_DIAMOND]      = cav->diamond_score;
4763   level->score[SC_ROBOT]        = cav->alien_score;
4764   level->score[SC_SPACESHIP]    = cav->tank_score;
4765   level->score[SC_BUG]          = cav->bug_score;
4766   level->score[SC_YAMYAM]       = cav->eater_score;
4767   level->score[SC_NUT]          = cav->nut_score;
4768   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4769   level->score[SC_KEY]          = cav->key_score;
4770   level->score[SC_TIME_BONUS]   = cav->exit_score;
4771
4772   level->num_yamyam_contents    = cav->num_eater_arrays;
4773
4774   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4775     for (y = 0; y < 3; y++)
4776       for (x = 0; x < 3; x++)
4777         level->yamyam_content[i].e[x][y] =
4778           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4779
4780   level->amoeba_speed           = cav->amoeba_time;
4781   level->time_magic_wall        = cav->wonderwall_time;
4782   level->time_wheel             = cav->wheel_time;
4783
4784   level->android_move_time      = cav->android_move_time;
4785   level->android_clone_time     = cav->android_clone_time;
4786   level->ball_random            = cav->ball_random;
4787   level->ball_active_initial    = cav->ball_active;
4788   level->ball_time              = cav->ball_time;
4789   level->num_ball_contents      = cav->num_ball_arrays;
4790
4791   level->lenses_score           = cav->lenses_score;
4792   level->magnify_score          = cav->magnify_score;
4793   level->slurp_score            = cav->slurp_score;
4794
4795   level->lenses_time            = cav->lenses_time;
4796   level->magnify_time           = cav->magnify_time;
4797
4798   level->wind_direction_initial =
4799     map_direction_EM_to_RND(cav->wind_direction);
4800
4801   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4802     for (j = 0; j < 8; j++)
4803       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4804         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4805
4806   map_android_clone_elements_EM_to_RND(level);
4807
4808   // convert the playfield (some elements need special treatment)
4809   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4810   {
4811     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4812
4813     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4814       new_element = EL_AMOEBA_DEAD;
4815
4816     level->field[x][y] = new_element;
4817   }
4818
4819   for (i = 0; i < MAX_PLAYERS; i++)
4820   {
4821     // in case of all players set to the same field, use the first player
4822     int nr = MAX_PLAYERS - i - 1;
4823     int jx = cav->player_x[nr];
4824     int jy = cav->player_y[nr];
4825
4826     if (jx != -1 && jy != -1)
4827       level->field[jx][jy] = EL_PLAYER_1 + nr;
4828   }
4829
4830   // time score is counted for each 10 seconds left in Emerald Mine levels
4831   level->time_score_base = 10;
4832 }
4833
4834
4835 // ----------------------------------------------------------------------------
4836 // functions for loading SP level
4837 // ----------------------------------------------------------------------------
4838
4839 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4840 {
4841   struct LevelInfo_SP *level_sp = level->native_sp_level;
4842   LevelInfoType *header = &level_sp->header;
4843   int i, x, y;
4844
4845   level_sp->width  = level->fieldx;
4846   level_sp->height = level->fieldy;
4847
4848   for (x = 0; x < level->fieldx; x++)
4849     for (y = 0; y < level->fieldy; y++)
4850       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4851
4852   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4853
4854   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4855     header->LevelTitle[i] = level->name[i];
4856   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4857
4858   header->InfotronsNeeded = level->gems_needed;
4859
4860   header->SpecialPortCount = 0;
4861
4862   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4863   {
4864     boolean gravity_port_found = FALSE;
4865     boolean gravity_port_valid = FALSE;
4866     int gravity_port_flag;
4867     int gravity_port_base_element;
4868     int element = level->field[x][y];
4869
4870     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4871         element <= EL_SP_GRAVITY_ON_PORT_UP)
4872     {
4873       gravity_port_found = TRUE;
4874       gravity_port_valid = TRUE;
4875       gravity_port_flag = 1;
4876       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4877     }
4878     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4879              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4880     {
4881       gravity_port_found = TRUE;
4882       gravity_port_valid = TRUE;
4883       gravity_port_flag = 0;
4884       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4885     }
4886     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4887              element <= EL_SP_GRAVITY_PORT_UP)
4888     {
4889       // change R'n'D style gravity inverting special port to normal port
4890       // (there are no gravity inverting ports in native Supaplex engine)
4891
4892       gravity_port_found = TRUE;
4893       gravity_port_valid = FALSE;
4894       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4895     }
4896
4897     if (gravity_port_found)
4898     {
4899       if (gravity_port_valid &&
4900           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4901       {
4902         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4903
4904         port->PortLocation = (y * level->fieldx + x) * 2;
4905         port->Gravity = gravity_port_flag;
4906
4907         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4908
4909         header->SpecialPortCount++;
4910       }
4911       else
4912       {
4913         // change special gravity port to normal port
4914
4915         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4916       }
4917
4918       level_sp->playfield[x][y] = element - EL_SP_START;
4919     }
4920   }
4921 }
4922
4923 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4924 {
4925   struct LevelInfo_SP *level_sp = level->native_sp_level;
4926   LevelInfoType *header = &level_sp->header;
4927   boolean num_invalid_elements = 0;
4928   int i, j, x, y;
4929
4930   level->fieldx = level_sp->width;
4931   level->fieldy = level_sp->height;
4932
4933   for (x = 0; x < level->fieldx; x++)
4934   {
4935     for (y = 0; y < level->fieldy; y++)
4936     {
4937       int element_old = level_sp->playfield[x][y];
4938       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4939
4940       if (element_new == EL_UNKNOWN)
4941       {
4942         num_invalid_elements++;
4943
4944         Debug("level:native:SP", "invalid element %d at position %d, %d",
4945               element_old, x, y);
4946       }
4947
4948       level->field[x][y] = element_new;
4949     }
4950   }
4951
4952   if (num_invalid_elements > 0)
4953     Warn("found %d invalid elements%s", num_invalid_elements,
4954          (!options.debug ? " (use '--debug' for more details)" : ""));
4955
4956   for (i = 0; i < MAX_PLAYERS; i++)
4957     level->initial_player_gravity[i] =
4958       (header->InitialGravity == 1 ? TRUE : FALSE);
4959
4960   // skip leading spaces
4961   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4962     if (header->LevelTitle[i] != ' ')
4963       break;
4964
4965   // copy level title
4966   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4967     level->name[j] = header->LevelTitle[i];
4968   level->name[j] = '\0';
4969
4970   // cut trailing spaces
4971   for (; j > 0; j--)
4972     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4973       level->name[j - 1] = '\0';
4974
4975   level->gems_needed = header->InfotronsNeeded;
4976
4977   for (i = 0; i < header->SpecialPortCount; i++)
4978   {
4979     SpecialPortType *port = &header->SpecialPort[i];
4980     int port_location = port->PortLocation;
4981     int gravity = port->Gravity;
4982     int port_x, port_y, port_element;
4983
4984     port_x = (port_location / 2) % level->fieldx;
4985     port_y = (port_location / 2) / level->fieldx;
4986
4987     if (port_x < 0 || port_x >= level->fieldx ||
4988         port_y < 0 || port_y >= level->fieldy)
4989     {
4990       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4991
4992       continue;
4993     }
4994
4995     port_element = level->field[port_x][port_y];
4996
4997     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4998         port_element > EL_SP_GRAVITY_PORT_UP)
4999     {
5000       Warn("no special port at position (%d, %d)", port_x, port_y);
5001
5002       continue;
5003     }
5004
5005     // change previous (wrong) gravity inverting special port to either
5006     // gravity enabling special port or gravity disabling special port
5007     level->field[port_x][port_y] +=
5008       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5009        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5010   }
5011
5012   // change special gravity ports without database entries to normal ports
5013   for (x = 0; x < level->fieldx; x++)
5014     for (y = 0; y < level->fieldy; y++)
5015       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5016           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5017         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5018
5019   level->time = 0;                      // no time limit
5020   level->amoeba_speed = 0;
5021   level->time_magic_wall = 0;
5022   level->time_wheel = 0;
5023   level->amoeba_content = EL_EMPTY;
5024
5025   // original Supaplex does not use score values -- rate by playing time
5026   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5027     level->score[i] = 0;
5028
5029   level->rate_time_over_score = TRUE;
5030
5031   // there are no yamyams in supaplex levels
5032   for (i = 0; i < level->num_yamyam_contents; i++)
5033     for (x = 0; x < 3; x++)
5034       for (y = 0; y < 3; y++)
5035         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5036 }
5037
5038 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5039 {
5040   struct LevelInfo_SP *level_sp = level->native_sp_level;
5041   struct DemoInfo_SP *demo = &level_sp->demo;
5042   int i, j;
5043
5044   // always start with reliable default values
5045   demo->is_available = FALSE;
5046   demo->length = 0;
5047
5048   if (TAPE_IS_EMPTY(tape))
5049     return;
5050
5051   demo->level_nr = tape.level_nr;       // (currently not used)
5052
5053   level_sp->header.DemoRandomSeed = tape.random_seed;
5054
5055   demo->length = 0;
5056
5057   for (i = 0; i < tape.length; i++)
5058   {
5059     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5060     int demo_repeat = tape.pos[i].delay;
5061     int demo_entries = (demo_repeat + 15) / 16;
5062
5063     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5064     {
5065       Warn("tape truncated: size exceeds maximum SP demo size %d",
5066            SP_MAX_TAPE_LEN);
5067
5068       break;
5069     }
5070
5071     for (j = 0; j < demo_repeat / 16; j++)
5072       demo->data[demo->length++] = 0xf0 | demo_action;
5073
5074     if (demo_repeat % 16)
5075       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5076   }
5077
5078   demo->is_available = TRUE;
5079 }
5080
5081 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5082 {
5083   struct LevelInfo_SP *level_sp = level->native_sp_level;
5084   struct DemoInfo_SP *demo = &level_sp->demo;
5085   char *filename = level->file_info.filename;
5086   int i;
5087
5088   // always start with reliable default values
5089   setTapeInfoToDefaults();
5090
5091   if (!demo->is_available)
5092     return;
5093
5094   tape.level_nr = demo->level_nr;       // (currently not used)
5095   tape.random_seed = level_sp->header.DemoRandomSeed;
5096
5097   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5098
5099   tape.counter = 0;
5100   tape.pos[tape.counter].delay = 0;
5101
5102   for (i = 0; i < demo->length; i++)
5103   {
5104     int demo_action = demo->data[i] & 0x0f;
5105     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5106     int tape_action = map_key_SP_to_RND(demo_action);
5107     int tape_repeat = demo_repeat + 1;
5108     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5109     boolean success = 0;
5110     int j;
5111
5112     for (j = 0; j < tape_repeat; j++)
5113       success = TapeAddAction(action);
5114
5115     if (!success)
5116     {
5117       Warn("SP demo truncated: size exceeds maximum tape size %d",
5118            MAX_TAPE_LEN);
5119
5120       break;
5121     }
5122   }
5123
5124   TapeHaltRecording();
5125 }
5126
5127
5128 // ----------------------------------------------------------------------------
5129 // functions for loading MM level
5130 // ----------------------------------------------------------------------------
5131
5132 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5133 {
5134   struct LevelInfo_MM *level_mm = level->native_mm_level;
5135   int i, x, y;
5136
5137   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5138   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5139
5140   level_mm->time = level->time;
5141   level_mm->kettles_needed = level->gems_needed;
5142   level_mm->auto_count_kettles = level->auto_count_gems;
5143
5144   level_mm->mm_laser_red   = level->mm_laser_red;
5145   level_mm->mm_laser_green = level->mm_laser_green;
5146   level_mm->mm_laser_blue  = level->mm_laser_blue;
5147
5148   level_mm->df_laser_red   = level->df_laser_red;
5149   level_mm->df_laser_green = level->df_laser_green;
5150   level_mm->df_laser_blue  = level->df_laser_blue;
5151
5152   strcpy(level_mm->name, level->name);
5153   strcpy(level_mm->author, level->author);
5154
5155   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5156   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5157   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5158   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5159   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5160
5161   level_mm->amoeba_speed = level->amoeba_speed;
5162   level_mm->time_fuse    = level->mm_time_fuse;
5163   level_mm->time_bomb    = level->mm_time_bomb;
5164   level_mm->time_ball    = level->mm_time_ball;
5165   level_mm->time_block   = level->mm_time_block;
5166
5167   level_mm->num_ball_contents = level->num_mm_ball_contents;
5168   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5169   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5170   level_mm->explode_ball = level->explode_mm_ball;
5171
5172   for (i = 0; i < level->num_mm_ball_contents; i++)
5173     level_mm->ball_content[i] =
5174       map_element_RND_to_MM(level->mm_ball_content[i]);
5175
5176   for (x = 0; x < level->fieldx; x++)
5177     for (y = 0; y < level->fieldy; y++)
5178       Ur[x][y] =
5179         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5180 }
5181
5182 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5183 {
5184   struct LevelInfo_MM *level_mm = level->native_mm_level;
5185   int i, x, y;
5186
5187   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5188   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5189
5190   level->time = level_mm->time;
5191   level->gems_needed = level_mm->kettles_needed;
5192   level->auto_count_gems = level_mm->auto_count_kettles;
5193
5194   level->mm_laser_red   = level_mm->mm_laser_red;
5195   level->mm_laser_green = level_mm->mm_laser_green;
5196   level->mm_laser_blue  = level_mm->mm_laser_blue;
5197
5198   level->df_laser_red   = level_mm->df_laser_red;
5199   level->df_laser_green = level_mm->df_laser_green;
5200   level->df_laser_blue  = level_mm->df_laser_blue;
5201
5202   strcpy(level->name, level_mm->name);
5203
5204   // only overwrite author from 'levelinfo.conf' if author defined in level
5205   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5206     strcpy(level->author, level_mm->author);
5207
5208   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5209   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5210   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5211   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5212   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5213
5214   level->amoeba_speed  = level_mm->amoeba_speed;
5215   level->mm_time_fuse  = level_mm->time_fuse;
5216   level->mm_time_bomb  = level_mm->time_bomb;
5217   level->mm_time_ball  = level_mm->time_ball;
5218   level->mm_time_block = level_mm->time_block;
5219
5220   level->num_mm_ball_contents = level_mm->num_ball_contents;
5221   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5222   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5223   level->explode_mm_ball = level_mm->explode_ball;
5224
5225   for (i = 0; i < level->num_mm_ball_contents; i++)
5226     level->mm_ball_content[i] =
5227       map_element_MM_to_RND(level_mm->ball_content[i]);
5228
5229   for (x = 0; x < level->fieldx; x++)
5230     for (y = 0; y < level->fieldy; y++)
5231       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5232 }
5233
5234
5235 // ----------------------------------------------------------------------------
5236 // functions for loading DC level
5237 // ----------------------------------------------------------------------------
5238
5239 #define DC_LEVEL_HEADER_SIZE            344
5240
5241 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5242                                         boolean init)
5243 {
5244   static int last_data_encoded;
5245   static int offset1;
5246   static int offset2;
5247   int diff;
5248   int diff_hi, diff_lo;
5249   int data_hi, data_lo;
5250   unsigned short data_decoded;
5251
5252   if (init)
5253   {
5254     last_data_encoded = 0;
5255     offset1 = -1;
5256     offset2 = 0;
5257
5258     return 0;
5259   }
5260
5261   diff = data_encoded - last_data_encoded;
5262   diff_hi = diff & ~0xff;
5263   diff_lo = diff &  0xff;
5264
5265   offset2 += diff_lo;
5266
5267   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5268   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5269   data_hi = data_hi & 0xff00;
5270
5271   data_decoded = data_hi | data_lo;
5272
5273   last_data_encoded = data_encoded;
5274
5275   offset1 = (offset1 + 1) % 31;
5276   offset2 = offset2 & 0xff;
5277
5278   return data_decoded;
5279 }
5280
5281 static int getMappedElement_DC(int element)
5282 {
5283   switch (element)
5284   {
5285     case 0x0000:
5286       element = EL_ROCK;
5287       break;
5288
5289       // 0x0117 - 0x036e: (?)
5290       // EL_DIAMOND
5291
5292       // 0x042d - 0x0684: (?)
5293       // EL_EMERALD
5294
5295     case 0x06f1:
5296       element = EL_NUT;
5297       break;
5298
5299     case 0x074c:
5300       element = EL_BOMB;
5301       break;
5302
5303     case 0x07a4:
5304       element = EL_PEARL;
5305       break;
5306
5307     case 0x0823:
5308       element = EL_CRYSTAL;
5309       break;
5310
5311     case 0x0e77:        // quicksand (boulder)
5312       element = EL_QUICKSAND_FAST_FULL;
5313       break;
5314
5315     case 0x0e99:        // slow quicksand (boulder)
5316       element = EL_QUICKSAND_FULL;
5317       break;
5318
5319     case 0x0ed2:
5320       element = EL_EM_EXIT_OPEN;
5321       break;
5322
5323     case 0x0ee3:
5324       element = EL_EM_EXIT_CLOSED;
5325       break;
5326
5327     case 0x0eeb:
5328       element = EL_EM_STEEL_EXIT_OPEN;
5329       break;
5330
5331     case 0x0efc:
5332       element = EL_EM_STEEL_EXIT_CLOSED;
5333       break;
5334
5335     case 0x0f4f:        // dynamite (lit 1)
5336       element = EL_EM_DYNAMITE_ACTIVE;
5337       break;
5338
5339     case 0x0f57:        // dynamite (lit 2)
5340       element = EL_EM_DYNAMITE_ACTIVE;
5341       break;
5342
5343     case 0x0f5f:        // dynamite (lit 3)
5344       element = EL_EM_DYNAMITE_ACTIVE;
5345       break;
5346
5347     case 0x0f67:        // dynamite (lit 4)
5348       element = EL_EM_DYNAMITE_ACTIVE;
5349       break;
5350
5351     case 0x0f81:
5352     case 0x0f82:
5353     case 0x0f83:
5354     case 0x0f84:
5355       element = EL_AMOEBA_WET;
5356       break;
5357
5358     case 0x0f85:
5359       element = EL_AMOEBA_DROP;
5360       break;
5361
5362     case 0x0fb9:
5363       element = EL_DC_MAGIC_WALL;
5364       break;
5365
5366     case 0x0fd0:
5367       element = EL_SPACESHIP_UP;
5368       break;
5369
5370     case 0x0fd9:
5371       element = EL_SPACESHIP_DOWN;
5372       break;
5373
5374     case 0x0ff1:
5375       element = EL_SPACESHIP_LEFT;
5376       break;
5377
5378     case 0x0ff9:
5379       element = EL_SPACESHIP_RIGHT;
5380       break;
5381
5382     case 0x1057:
5383       element = EL_BUG_UP;
5384       break;
5385
5386     case 0x1060:
5387       element = EL_BUG_DOWN;
5388       break;
5389
5390     case 0x1078:
5391       element = EL_BUG_LEFT;
5392       break;
5393
5394     case 0x1080:
5395       element = EL_BUG_RIGHT;
5396       break;
5397
5398     case 0x10de:
5399       element = EL_MOLE_UP;
5400       break;
5401
5402     case 0x10e7:
5403       element = EL_MOLE_DOWN;
5404       break;
5405
5406     case 0x10ff:
5407       element = EL_MOLE_LEFT;
5408       break;
5409
5410     case 0x1107:
5411       element = EL_MOLE_RIGHT;
5412       break;
5413
5414     case 0x11c0:
5415       element = EL_ROBOT;
5416       break;
5417
5418     case 0x13f5:
5419       element = EL_YAMYAM_UP;
5420       break;
5421
5422     case 0x1425:
5423       element = EL_SWITCHGATE_OPEN;
5424       break;
5425
5426     case 0x1426:
5427       element = EL_SWITCHGATE_CLOSED;
5428       break;
5429
5430     case 0x1437:
5431       element = EL_DC_SWITCHGATE_SWITCH_UP;
5432       break;
5433
5434     case 0x143a:
5435       element = EL_TIMEGATE_CLOSED;
5436       break;
5437
5438     case 0x144c:        // conveyor belt switch (green)
5439       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5440       break;
5441
5442     case 0x144f:        // conveyor belt switch (red)
5443       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5444       break;
5445
5446     case 0x1452:        // conveyor belt switch (blue)
5447       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5448       break;
5449
5450     case 0x145b:
5451       element = EL_CONVEYOR_BELT_3_MIDDLE;
5452       break;
5453
5454     case 0x1463:
5455       element = EL_CONVEYOR_BELT_3_LEFT;
5456       break;
5457
5458     case 0x146b:
5459       element = EL_CONVEYOR_BELT_3_RIGHT;
5460       break;
5461
5462     case 0x1473:
5463       element = EL_CONVEYOR_BELT_1_MIDDLE;
5464       break;
5465
5466     case 0x147b:
5467       element = EL_CONVEYOR_BELT_1_LEFT;
5468       break;
5469
5470     case 0x1483:
5471       element = EL_CONVEYOR_BELT_1_RIGHT;
5472       break;
5473
5474     case 0x148b:
5475       element = EL_CONVEYOR_BELT_4_MIDDLE;
5476       break;
5477
5478     case 0x1493:
5479       element = EL_CONVEYOR_BELT_4_LEFT;
5480       break;
5481
5482     case 0x149b:
5483       element = EL_CONVEYOR_BELT_4_RIGHT;
5484       break;
5485
5486     case 0x14ac:
5487       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5488       break;
5489
5490     case 0x14bd:
5491       element = EL_EXPANDABLE_WALL_VERTICAL;
5492       break;
5493
5494     case 0x14c6:
5495       element = EL_EXPANDABLE_WALL_ANY;
5496       break;
5497
5498     case 0x14ce:        // growing steel wall (left/right)
5499       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5500       break;
5501
5502     case 0x14df:        // growing steel wall (up/down)
5503       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5504       break;
5505
5506     case 0x14e8:        // growing steel wall (up/down/left/right)
5507       element = EL_EXPANDABLE_STEELWALL_ANY;
5508       break;
5509
5510     case 0x14e9:
5511       element = EL_SHIELD_DEADLY;
5512       break;
5513
5514     case 0x1501:
5515       element = EL_EXTRA_TIME;
5516       break;
5517
5518     case 0x154f:
5519       element = EL_ACID;
5520       break;
5521
5522     case 0x1577:
5523       element = EL_EMPTY_SPACE;
5524       break;
5525
5526     case 0x1578:        // quicksand (empty)
5527       element = EL_QUICKSAND_FAST_EMPTY;
5528       break;
5529
5530     case 0x1579:        // slow quicksand (empty)
5531       element = EL_QUICKSAND_EMPTY;
5532       break;
5533
5534       // 0x157c - 0x158b:
5535       // EL_SAND
5536
5537       // 0x1590 - 0x159f:
5538       // EL_DC_LANDMINE
5539
5540     case 0x15a0:
5541       element = EL_EM_DYNAMITE;
5542       break;
5543
5544     case 0x15a1:        // key (red)
5545       element = EL_EM_KEY_1;
5546       break;
5547
5548     case 0x15a2:        // key (yellow)
5549       element = EL_EM_KEY_2;
5550       break;
5551
5552     case 0x15a3:        // key (blue)
5553       element = EL_EM_KEY_4;
5554       break;
5555
5556     case 0x15a4:        // key (green)
5557       element = EL_EM_KEY_3;
5558       break;
5559
5560     case 0x15a5:        // key (white)
5561       element = EL_DC_KEY_WHITE;
5562       break;
5563
5564     case 0x15a6:
5565       element = EL_WALL_SLIPPERY;
5566       break;
5567
5568     case 0x15a7:
5569       element = EL_WALL;
5570       break;
5571
5572     case 0x15a8:        // wall (not round)
5573       element = EL_WALL;
5574       break;
5575
5576     case 0x15a9:        // (blue)
5577       element = EL_CHAR_A;
5578       break;
5579
5580     case 0x15aa:        // (blue)
5581       element = EL_CHAR_B;
5582       break;
5583
5584     case 0x15ab:        // (blue)
5585       element = EL_CHAR_C;
5586       break;
5587
5588     case 0x15ac:        // (blue)
5589       element = EL_CHAR_D;
5590       break;
5591
5592     case 0x15ad:        // (blue)
5593       element = EL_CHAR_E;
5594       break;
5595
5596     case 0x15ae:        // (blue)
5597       element = EL_CHAR_F;
5598       break;
5599
5600     case 0x15af:        // (blue)
5601       element = EL_CHAR_G;
5602       break;
5603
5604     case 0x15b0:        // (blue)
5605       element = EL_CHAR_H;
5606       break;
5607
5608     case 0x15b1:        // (blue)
5609       element = EL_CHAR_I;
5610       break;
5611
5612     case 0x15b2:        // (blue)
5613       element = EL_CHAR_J;
5614       break;
5615
5616     case 0x15b3:        // (blue)
5617       element = EL_CHAR_K;
5618       break;
5619
5620     case 0x15b4:        // (blue)
5621       element = EL_CHAR_L;
5622       break;
5623
5624     case 0x15b5:        // (blue)
5625       element = EL_CHAR_M;
5626       break;
5627
5628     case 0x15b6:        // (blue)
5629       element = EL_CHAR_N;
5630       break;
5631
5632     case 0x15b7:        // (blue)
5633       element = EL_CHAR_O;
5634       break;
5635
5636     case 0x15b8:        // (blue)
5637       element = EL_CHAR_P;
5638       break;
5639
5640     case 0x15b9:        // (blue)
5641       element = EL_CHAR_Q;
5642       break;
5643
5644     case 0x15ba:        // (blue)
5645       element = EL_CHAR_R;
5646       break;
5647
5648     case 0x15bb:        // (blue)
5649       element = EL_CHAR_S;
5650       break;
5651
5652     case 0x15bc:        // (blue)
5653       element = EL_CHAR_T;
5654       break;
5655
5656     case 0x15bd:        // (blue)
5657       element = EL_CHAR_U;
5658       break;
5659
5660     case 0x15be:        // (blue)
5661       element = EL_CHAR_V;
5662       break;
5663
5664     case 0x15bf:        // (blue)
5665       element = EL_CHAR_W;
5666       break;
5667
5668     case 0x15c0:        // (blue)
5669       element = EL_CHAR_X;
5670       break;
5671
5672     case 0x15c1:        // (blue)
5673       element = EL_CHAR_Y;
5674       break;
5675
5676     case 0x15c2:        // (blue)
5677       element = EL_CHAR_Z;
5678       break;
5679
5680     case 0x15c3:        // (blue)
5681       element = EL_CHAR_AUMLAUT;
5682       break;
5683
5684     case 0x15c4:        // (blue)
5685       element = EL_CHAR_OUMLAUT;
5686       break;
5687
5688     case 0x15c5:        // (blue)
5689       element = EL_CHAR_UUMLAUT;
5690       break;
5691
5692     case 0x15c6:        // (blue)
5693       element = EL_CHAR_0;
5694       break;
5695
5696     case 0x15c7:        // (blue)
5697       element = EL_CHAR_1;
5698       break;
5699
5700     case 0x15c8:        // (blue)
5701       element = EL_CHAR_2;
5702       break;
5703
5704     case 0x15c9:        // (blue)
5705       element = EL_CHAR_3;
5706       break;
5707
5708     case 0x15ca:        // (blue)
5709       element = EL_CHAR_4;
5710       break;
5711
5712     case 0x15cb:        // (blue)
5713       element = EL_CHAR_5;
5714       break;
5715
5716     case 0x15cc:        // (blue)
5717       element = EL_CHAR_6;
5718       break;
5719
5720     case 0x15cd:        // (blue)
5721       element = EL_CHAR_7;
5722       break;
5723
5724     case 0x15ce:        // (blue)
5725       element = EL_CHAR_8;
5726       break;
5727
5728     case 0x15cf:        // (blue)
5729       element = EL_CHAR_9;
5730       break;
5731
5732     case 0x15d0:        // (blue)
5733       element = EL_CHAR_PERIOD;
5734       break;
5735
5736     case 0x15d1:        // (blue)
5737       element = EL_CHAR_EXCLAM;
5738       break;
5739
5740     case 0x15d2:        // (blue)
5741       element = EL_CHAR_COLON;
5742       break;
5743
5744     case 0x15d3:        // (blue)
5745       element = EL_CHAR_LESS;
5746       break;
5747
5748     case 0x15d4:        // (blue)
5749       element = EL_CHAR_GREATER;
5750       break;
5751
5752     case 0x15d5:        // (blue)
5753       element = EL_CHAR_QUESTION;
5754       break;
5755
5756     case 0x15d6:        // (blue)
5757       element = EL_CHAR_COPYRIGHT;
5758       break;
5759
5760     case 0x15d7:        // (blue)
5761       element = EL_CHAR_UP;
5762       break;
5763
5764     case 0x15d8:        // (blue)
5765       element = EL_CHAR_DOWN;
5766       break;
5767
5768     case 0x15d9:        // (blue)
5769       element = EL_CHAR_BUTTON;
5770       break;
5771
5772     case 0x15da:        // (blue)
5773       element = EL_CHAR_PLUS;
5774       break;
5775
5776     case 0x15db:        // (blue)
5777       element = EL_CHAR_MINUS;
5778       break;
5779
5780     case 0x15dc:        // (blue)
5781       element = EL_CHAR_APOSTROPHE;
5782       break;
5783
5784     case 0x15dd:        // (blue)
5785       element = EL_CHAR_PARENLEFT;
5786       break;
5787
5788     case 0x15de:        // (blue)
5789       element = EL_CHAR_PARENRIGHT;
5790       break;
5791
5792     case 0x15df:        // (green)
5793       element = EL_CHAR_A;
5794       break;
5795
5796     case 0x15e0:        // (green)
5797       element = EL_CHAR_B;
5798       break;
5799
5800     case 0x15e1:        // (green)
5801       element = EL_CHAR_C;
5802       break;
5803
5804     case 0x15e2:        // (green)
5805       element = EL_CHAR_D;
5806       break;
5807
5808     case 0x15e3:        // (green)
5809       element = EL_CHAR_E;
5810       break;
5811
5812     case 0x15e4:        // (green)
5813       element = EL_CHAR_F;
5814       break;
5815
5816     case 0x15e5:        // (green)
5817       element = EL_CHAR_G;
5818       break;
5819
5820     case 0x15e6:        // (green)
5821       element = EL_CHAR_H;
5822       break;
5823
5824     case 0x15e7:        // (green)
5825       element = EL_CHAR_I;
5826       break;
5827
5828     case 0x15e8:        // (green)
5829       element = EL_CHAR_J;
5830       break;
5831
5832     case 0x15e9:        // (green)
5833       element = EL_CHAR_K;
5834       break;
5835
5836     case 0x15ea:        // (green)
5837       element = EL_CHAR_L;
5838       break;
5839
5840     case 0x15eb:        // (green)
5841       element = EL_CHAR_M;
5842       break;
5843
5844     case 0x15ec:        // (green)
5845       element = EL_CHAR_N;
5846       break;
5847
5848     case 0x15ed:        // (green)
5849       element = EL_CHAR_O;
5850       break;
5851
5852     case 0x15ee:        // (green)
5853       element = EL_CHAR_P;
5854       break;
5855
5856     case 0x15ef:        // (green)
5857       element = EL_CHAR_Q;
5858       break;
5859
5860     case 0x15f0:        // (green)
5861       element = EL_CHAR_R;
5862       break;
5863
5864     case 0x15f1:        // (green)
5865       element = EL_CHAR_S;
5866       break;
5867
5868     case 0x15f2:        // (green)
5869       element = EL_CHAR_T;
5870       break;
5871
5872     case 0x15f3:        // (green)
5873       element = EL_CHAR_U;
5874       break;
5875
5876     case 0x15f4:        // (green)
5877       element = EL_CHAR_V;
5878       break;
5879
5880     case 0x15f5:        // (green)
5881       element = EL_CHAR_W;
5882       break;
5883
5884     case 0x15f6:        // (green)
5885       element = EL_CHAR_X;
5886       break;
5887
5888     case 0x15f7:        // (green)
5889       element = EL_CHAR_Y;
5890       break;
5891
5892     case 0x15f8:        // (green)
5893       element = EL_CHAR_Z;
5894       break;
5895
5896     case 0x15f9:        // (green)
5897       element = EL_CHAR_AUMLAUT;
5898       break;
5899
5900     case 0x15fa:        // (green)
5901       element = EL_CHAR_OUMLAUT;
5902       break;
5903
5904     case 0x15fb:        // (green)
5905       element = EL_CHAR_UUMLAUT;
5906       break;
5907
5908     case 0x15fc:        // (green)
5909       element = EL_CHAR_0;
5910       break;
5911
5912     case 0x15fd:        // (green)
5913       element = EL_CHAR_1;
5914       break;
5915
5916     case 0x15fe:        // (green)
5917       element = EL_CHAR_2;
5918       break;
5919
5920     case 0x15ff:        // (green)
5921       element = EL_CHAR_3;
5922       break;
5923
5924     case 0x1600:        // (green)
5925       element = EL_CHAR_4;
5926       break;
5927
5928     case 0x1601:        // (green)
5929       element = EL_CHAR_5;
5930       break;
5931
5932     case 0x1602:        // (green)
5933       element = EL_CHAR_6;
5934       break;
5935
5936     case 0x1603:        // (green)
5937       element = EL_CHAR_7;
5938       break;
5939
5940     case 0x1604:        // (green)
5941       element = EL_CHAR_8;
5942       break;
5943
5944     case 0x1605:        // (green)
5945       element = EL_CHAR_9;
5946       break;
5947
5948     case 0x1606:        // (green)
5949       element = EL_CHAR_PERIOD;
5950       break;
5951
5952     case 0x1607:        // (green)
5953       element = EL_CHAR_EXCLAM;
5954       break;
5955
5956     case 0x1608:        // (green)
5957       element = EL_CHAR_COLON;
5958       break;
5959
5960     case 0x1609:        // (green)
5961       element = EL_CHAR_LESS;
5962       break;
5963
5964     case 0x160a:        // (green)
5965       element = EL_CHAR_GREATER;
5966       break;
5967
5968     case 0x160b:        // (green)
5969       element = EL_CHAR_QUESTION;
5970       break;
5971
5972     case 0x160c:        // (green)
5973       element = EL_CHAR_COPYRIGHT;
5974       break;
5975
5976     case 0x160d:        // (green)
5977       element = EL_CHAR_UP;
5978       break;
5979
5980     case 0x160e:        // (green)
5981       element = EL_CHAR_DOWN;
5982       break;
5983
5984     case 0x160f:        // (green)
5985       element = EL_CHAR_BUTTON;
5986       break;
5987
5988     case 0x1610:        // (green)
5989       element = EL_CHAR_PLUS;
5990       break;
5991
5992     case 0x1611:        // (green)
5993       element = EL_CHAR_MINUS;
5994       break;
5995
5996     case 0x1612:        // (green)
5997       element = EL_CHAR_APOSTROPHE;
5998       break;
5999
6000     case 0x1613:        // (green)
6001       element = EL_CHAR_PARENLEFT;
6002       break;
6003
6004     case 0x1614:        // (green)
6005       element = EL_CHAR_PARENRIGHT;
6006       break;
6007
6008     case 0x1615:        // (blue steel)
6009       element = EL_STEEL_CHAR_A;
6010       break;
6011
6012     case 0x1616:        // (blue steel)
6013       element = EL_STEEL_CHAR_B;
6014       break;
6015
6016     case 0x1617:        // (blue steel)
6017       element = EL_STEEL_CHAR_C;
6018       break;
6019
6020     case 0x1618:        // (blue steel)
6021       element = EL_STEEL_CHAR_D;
6022       break;
6023
6024     case 0x1619:        // (blue steel)
6025       element = EL_STEEL_CHAR_E;
6026       break;
6027
6028     case 0x161a:        // (blue steel)
6029       element = EL_STEEL_CHAR_F;
6030       break;
6031
6032     case 0x161b:        // (blue steel)
6033       element = EL_STEEL_CHAR_G;
6034       break;
6035
6036     case 0x161c:        // (blue steel)
6037       element = EL_STEEL_CHAR_H;
6038       break;
6039
6040     case 0x161d:        // (blue steel)
6041       element = EL_STEEL_CHAR_I;
6042       break;
6043
6044     case 0x161e:        // (blue steel)
6045       element = EL_STEEL_CHAR_J;
6046       break;
6047
6048     case 0x161f:        // (blue steel)
6049       element = EL_STEEL_CHAR_K;
6050       break;
6051
6052     case 0x1620:        // (blue steel)
6053       element = EL_STEEL_CHAR_L;
6054       break;
6055
6056     case 0x1621:        // (blue steel)
6057       element = EL_STEEL_CHAR_M;
6058       break;
6059
6060     case 0x1622:        // (blue steel)
6061       element = EL_STEEL_CHAR_N;
6062       break;
6063
6064     case 0x1623:        // (blue steel)
6065       element = EL_STEEL_CHAR_O;
6066       break;
6067
6068     case 0x1624:        // (blue steel)
6069       element = EL_STEEL_CHAR_P;
6070       break;
6071
6072     case 0x1625:        // (blue steel)
6073       element = EL_STEEL_CHAR_Q;
6074       break;
6075
6076     case 0x1626:        // (blue steel)
6077       element = EL_STEEL_CHAR_R;
6078       break;
6079
6080     case 0x1627:        // (blue steel)
6081       element = EL_STEEL_CHAR_S;
6082       break;
6083
6084     case 0x1628:        // (blue steel)
6085       element = EL_STEEL_CHAR_T;
6086       break;
6087
6088     case 0x1629:        // (blue steel)
6089       element = EL_STEEL_CHAR_U;
6090       break;
6091
6092     case 0x162a:        // (blue steel)
6093       element = EL_STEEL_CHAR_V;
6094       break;
6095
6096     case 0x162b:        // (blue steel)
6097       element = EL_STEEL_CHAR_W;
6098       break;
6099
6100     case 0x162c:        // (blue steel)
6101       element = EL_STEEL_CHAR_X;
6102       break;
6103
6104     case 0x162d:        // (blue steel)
6105       element = EL_STEEL_CHAR_Y;
6106       break;
6107
6108     case 0x162e:        // (blue steel)
6109       element = EL_STEEL_CHAR_Z;
6110       break;
6111
6112     case 0x162f:        // (blue steel)
6113       element = EL_STEEL_CHAR_AUMLAUT;
6114       break;
6115
6116     case 0x1630:        // (blue steel)
6117       element = EL_STEEL_CHAR_OUMLAUT;
6118       break;
6119
6120     case 0x1631:        // (blue steel)
6121       element = EL_STEEL_CHAR_UUMLAUT;
6122       break;
6123
6124     case 0x1632:        // (blue steel)
6125       element = EL_STEEL_CHAR_0;
6126       break;
6127
6128     case 0x1633:        // (blue steel)
6129       element = EL_STEEL_CHAR_1;
6130       break;
6131
6132     case 0x1634:        // (blue steel)
6133       element = EL_STEEL_CHAR_2;
6134       break;
6135
6136     case 0x1635:        // (blue steel)
6137       element = EL_STEEL_CHAR_3;
6138       break;
6139
6140     case 0x1636:        // (blue steel)
6141       element = EL_STEEL_CHAR_4;
6142       break;
6143
6144     case 0x1637:        // (blue steel)
6145       element = EL_STEEL_CHAR_5;
6146       break;
6147
6148     case 0x1638:        // (blue steel)
6149       element = EL_STEEL_CHAR_6;
6150       break;
6151
6152     case 0x1639:        // (blue steel)
6153       element = EL_STEEL_CHAR_7;
6154       break;
6155
6156     case 0x163a:        // (blue steel)
6157       element = EL_STEEL_CHAR_8;
6158       break;
6159
6160     case 0x163b:        // (blue steel)
6161       element = EL_STEEL_CHAR_9;
6162       break;
6163
6164     case 0x163c:        // (blue steel)
6165       element = EL_STEEL_CHAR_PERIOD;
6166       break;
6167
6168     case 0x163d:        // (blue steel)
6169       element = EL_STEEL_CHAR_EXCLAM;
6170       break;
6171
6172     case 0x163e:        // (blue steel)
6173       element = EL_STEEL_CHAR_COLON;
6174       break;
6175
6176     case 0x163f:        // (blue steel)
6177       element = EL_STEEL_CHAR_LESS;
6178       break;
6179
6180     case 0x1640:        // (blue steel)
6181       element = EL_STEEL_CHAR_GREATER;
6182       break;
6183
6184     case 0x1641:        // (blue steel)
6185       element = EL_STEEL_CHAR_QUESTION;
6186       break;
6187
6188     case 0x1642:        // (blue steel)
6189       element = EL_STEEL_CHAR_COPYRIGHT;
6190       break;
6191
6192     case 0x1643:        // (blue steel)
6193       element = EL_STEEL_CHAR_UP;
6194       break;
6195
6196     case 0x1644:        // (blue steel)
6197       element = EL_STEEL_CHAR_DOWN;
6198       break;
6199
6200     case 0x1645:        // (blue steel)
6201       element = EL_STEEL_CHAR_BUTTON;
6202       break;
6203
6204     case 0x1646:        // (blue steel)
6205       element = EL_STEEL_CHAR_PLUS;
6206       break;
6207
6208     case 0x1647:        // (blue steel)
6209       element = EL_STEEL_CHAR_MINUS;
6210       break;
6211
6212     case 0x1648:        // (blue steel)
6213       element = EL_STEEL_CHAR_APOSTROPHE;
6214       break;
6215
6216     case 0x1649:        // (blue steel)
6217       element = EL_STEEL_CHAR_PARENLEFT;
6218       break;
6219
6220     case 0x164a:        // (blue steel)
6221       element = EL_STEEL_CHAR_PARENRIGHT;
6222       break;
6223
6224     case 0x164b:        // (green steel)
6225       element = EL_STEEL_CHAR_A;
6226       break;
6227
6228     case 0x164c:        // (green steel)
6229       element = EL_STEEL_CHAR_B;
6230       break;
6231
6232     case 0x164d:        // (green steel)
6233       element = EL_STEEL_CHAR_C;
6234       break;
6235
6236     case 0x164e:        // (green steel)
6237       element = EL_STEEL_CHAR_D;
6238       break;
6239
6240     case 0x164f:        // (green steel)
6241       element = EL_STEEL_CHAR_E;
6242       break;
6243
6244     case 0x1650:        // (green steel)
6245       element = EL_STEEL_CHAR_F;
6246       break;
6247
6248     case 0x1651:        // (green steel)
6249       element = EL_STEEL_CHAR_G;
6250       break;
6251
6252     case 0x1652:        // (green steel)
6253       element = EL_STEEL_CHAR_H;
6254       break;
6255
6256     case 0x1653:        // (green steel)
6257       element = EL_STEEL_CHAR_I;
6258       break;
6259
6260     case 0x1654:        // (green steel)
6261       element = EL_STEEL_CHAR_J;
6262       break;
6263
6264     case 0x1655:        // (green steel)
6265       element = EL_STEEL_CHAR_K;
6266       break;
6267
6268     case 0x1656:        // (green steel)
6269       element = EL_STEEL_CHAR_L;
6270       break;
6271
6272     case 0x1657:        // (green steel)
6273       element = EL_STEEL_CHAR_M;
6274       break;
6275
6276     case 0x1658:        // (green steel)
6277       element = EL_STEEL_CHAR_N;
6278       break;
6279
6280     case 0x1659:        // (green steel)
6281       element = EL_STEEL_CHAR_O;
6282       break;
6283
6284     case 0x165a:        // (green steel)
6285       element = EL_STEEL_CHAR_P;
6286       break;
6287
6288     case 0x165b:        // (green steel)
6289       element = EL_STEEL_CHAR_Q;
6290       break;
6291
6292     case 0x165c:        // (green steel)
6293       element = EL_STEEL_CHAR_R;
6294       break;
6295
6296     case 0x165d:        // (green steel)
6297       element = EL_STEEL_CHAR_S;
6298       break;
6299
6300     case 0x165e:        // (green steel)
6301       element = EL_STEEL_CHAR_T;
6302       break;
6303
6304     case 0x165f:        // (green steel)
6305       element = EL_STEEL_CHAR_U;
6306       break;
6307
6308     case 0x1660:        // (green steel)
6309       element = EL_STEEL_CHAR_V;
6310       break;
6311
6312     case 0x1661:        // (green steel)
6313       element = EL_STEEL_CHAR_W;
6314       break;
6315
6316     case 0x1662:        // (green steel)
6317       element = EL_STEEL_CHAR_X;
6318       break;
6319
6320     case 0x1663:        // (green steel)
6321       element = EL_STEEL_CHAR_Y;
6322       break;
6323
6324     case 0x1664:        // (green steel)
6325       element = EL_STEEL_CHAR_Z;
6326       break;
6327
6328     case 0x1665:        // (green steel)
6329       element = EL_STEEL_CHAR_AUMLAUT;
6330       break;
6331
6332     case 0x1666:        // (green steel)
6333       element = EL_STEEL_CHAR_OUMLAUT;
6334       break;
6335
6336     case 0x1667:        // (green steel)
6337       element = EL_STEEL_CHAR_UUMLAUT;
6338       break;
6339
6340     case 0x1668:        // (green steel)
6341       element = EL_STEEL_CHAR_0;
6342       break;
6343
6344     case 0x1669:        // (green steel)
6345       element = EL_STEEL_CHAR_1;
6346       break;
6347
6348     case 0x166a:        // (green steel)
6349       element = EL_STEEL_CHAR_2;
6350       break;
6351
6352     case 0x166b:        // (green steel)
6353       element = EL_STEEL_CHAR_3;
6354       break;
6355
6356     case 0x166c:        // (green steel)
6357       element = EL_STEEL_CHAR_4;
6358       break;
6359
6360     case 0x166d:        // (green steel)
6361       element = EL_STEEL_CHAR_5;
6362       break;
6363
6364     case 0x166e:        // (green steel)
6365       element = EL_STEEL_CHAR_6;
6366       break;
6367
6368     case 0x166f:        // (green steel)
6369       element = EL_STEEL_CHAR_7;
6370       break;
6371
6372     case 0x1670:        // (green steel)
6373       element = EL_STEEL_CHAR_8;
6374       break;
6375
6376     case 0x1671:        // (green steel)
6377       element = EL_STEEL_CHAR_9;
6378       break;
6379
6380     case 0x1672:        // (green steel)
6381       element = EL_STEEL_CHAR_PERIOD;
6382       break;
6383
6384     case 0x1673:        // (green steel)
6385       element = EL_STEEL_CHAR_EXCLAM;
6386       break;
6387
6388     case 0x1674:        // (green steel)
6389       element = EL_STEEL_CHAR_COLON;
6390       break;
6391
6392     case 0x1675:        // (green steel)
6393       element = EL_STEEL_CHAR_LESS;
6394       break;
6395
6396     case 0x1676:        // (green steel)
6397       element = EL_STEEL_CHAR_GREATER;
6398       break;
6399
6400     case 0x1677:        // (green steel)
6401       element = EL_STEEL_CHAR_QUESTION;
6402       break;
6403
6404     case 0x1678:        // (green steel)
6405       element = EL_STEEL_CHAR_COPYRIGHT;
6406       break;
6407
6408     case 0x1679:        // (green steel)
6409       element = EL_STEEL_CHAR_UP;
6410       break;
6411
6412     case 0x167a:        // (green steel)
6413       element = EL_STEEL_CHAR_DOWN;
6414       break;
6415
6416     case 0x167b:        // (green steel)
6417       element = EL_STEEL_CHAR_BUTTON;
6418       break;
6419
6420     case 0x167c:        // (green steel)
6421       element = EL_STEEL_CHAR_PLUS;
6422       break;
6423
6424     case 0x167d:        // (green steel)
6425       element = EL_STEEL_CHAR_MINUS;
6426       break;
6427
6428     case 0x167e:        // (green steel)
6429       element = EL_STEEL_CHAR_APOSTROPHE;
6430       break;
6431
6432     case 0x167f:        // (green steel)
6433       element = EL_STEEL_CHAR_PARENLEFT;
6434       break;
6435
6436     case 0x1680:        // (green steel)
6437       element = EL_STEEL_CHAR_PARENRIGHT;
6438       break;
6439
6440     case 0x1681:        // gate (red)
6441       element = EL_EM_GATE_1;
6442       break;
6443
6444     case 0x1682:        // secret gate (red)
6445       element = EL_EM_GATE_1_GRAY;
6446       break;
6447
6448     case 0x1683:        // gate (yellow)
6449       element = EL_EM_GATE_2;
6450       break;
6451
6452     case 0x1684:        // secret gate (yellow)
6453       element = EL_EM_GATE_2_GRAY;
6454       break;
6455
6456     case 0x1685:        // gate (blue)
6457       element = EL_EM_GATE_4;
6458       break;
6459
6460     case 0x1686:        // secret gate (blue)
6461       element = EL_EM_GATE_4_GRAY;
6462       break;
6463
6464     case 0x1687:        // gate (green)
6465       element = EL_EM_GATE_3;
6466       break;
6467
6468     case 0x1688:        // secret gate (green)
6469       element = EL_EM_GATE_3_GRAY;
6470       break;
6471
6472     case 0x1689:        // gate (white)
6473       element = EL_DC_GATE_WHITE;
6474       break;
6475
6476     case 0x168a:        // secret gate (white)
6477       element = EL_DC_GATE_WHITE_GRAY;
6478       break;
6479
6480     case 0x168b:        // secret gate (no key)
6481       element = EL_DC_GATE_FAKE_GRAY;
6482       break;
6483
6484     case 0x168c:
6485       element = EL_ROBOT_WHEEL;
6486       break;
6487
6488     case 0x168d:
6489       element = EL_DC_TIMEGATE_SWITCH;
6490       break;
6491
6492     case 0x168e:
6493       element = EL_ACID_POOL_BOTTOM;
6494       break;
6495
6496     case 0x168f:
6497       element = EL_ACID_POOL_TOPLEFT;
6498       break;
6499
6500     case 0x1690:
6501       element = EL_ACID_POOL_TOPRIGHT;
6502       break;
6503
6504     case 0x1691:
6505       element = EL_ACID_POOL_BOTTOMLEFT;
6506       break;
6507
6508     case 0x1692:
6509       element = EL_ACID_POOL_BOTTOMRIGHT;
6510       break;
6511
6512     case 0x1693:
6513       element = EL_STEELWALL;
6514       break;
6515
6516     case 0x1694:
6517       element = EL_STEELWALL_SLIPPERY;
6518       break;
6519
6520     case 0x1695:        // steel wall (not round)
6521       element = EL_STEELWALL;
6522       break;
6523
6524     case 0x1696:        // steel wall (left)
6525       element = EL_DC_STEELWALL_1_LEFT;
6526       break;
6527
6528     case 0x1697:        // steel wall (bottom)
6529       element = EL_DC_STEELWALL_1_BOTTOM;
6530       break;
6531
6532     case 0x1698:        // steel wall (right)
6533       element = EL_DC_STEELWALL_1_RIGHT;
6534       break;
6535
6536     case 0x1699:        // steel wall (top)
6537       element = EL_DC_STEELWALL_1_TOP;
6538       break;
6539
6540     case 0x169a:        // steel wall (left/bottom)
6541       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6542       break;
6543
6544     case 0x169b:        // steel wall (right/bottom)
6545       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6546       break;
6547
6548     case 0x169c:        // steel wall (right/top)
6549       element = EL_DC_STEELWALL_1_TOPRIGHT;
6550       break;
6551
6552     case 0x169d:        // steel wall (left/top)
6553       element = EL_DC_STEELWALL_1_TOPLEFT;
6554       break;
6555
6556     case 0x169e:        // steel wall (right/bottom small)
6557       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6558       break;
6559
6560     case 0x169f:        // steel wall (left/bottom small)
6561       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6562       break;
6563
6564     case 0x16a0:        // steel wall (right/top small)
6565       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6566       break;
6567
6568     case 0x16a1:        // steel wall (left/top small)
6569       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6570       break;
6571
6572     case 0x16a2:        // steel wall (left/right)
6573       element = EL_DC_STEELWALL_1_VERTICAL;
6574       break;
6575
6576     case 0x16a3:        // steel wall (top/bottom)
6577       element = EL_DC_STEELWALL_1_HORIZONTAL;
6578       break;
6579
6580     case 0x16a4:        // steel wall 2 (left end)
6581       element = EL_DC_STEELWALL_2_LEFT;
6582       break;
6583
6584     case 0x16a5:        // steel wall 2 (right end)
6585       element = EL_DC_STEELWALL_2_RIGHT;
6586       break;
6587
6588     case 0x16a6:        // steel wall 2 (top end)
6589       element = EL_DC_STEELWALL_2_TOP;
6590       break;
6591
6592     case 0x16a7:        // steel wall 2 (bottom end)
6593       element = EL_DC_STEELWALL_2_BOTTOM;
6594       break;
6595
6596     case 0x16a8:        // steel wall 2 (left/right)
6597       element = EL_DC_STEELWALL_2_HORIZONTAL;
6598       break;
6599
6600     case 0x16a9:        // steel wall 2 (up/down)
6601       element = EL_DC_STEELWALL_2_VERTICAL;
6602       break;
6603
6604     case 0x16aa:        // steel wall 2 (mid)
6605       element = EL_DC_STEELWALL_2_MIDDLE;
6606       break;
6607
6608     case 0x16ab:
6609       element = EL_SIGN_EXCLAMATION;
6610       break;
6611
6612     case 0x16ac:
6613       element = EL_SIGN_RADIOACTIVITY;
6614       break;
6615
6616     case 0x16ad:
6617       element = EL_SIGN_STOP;
6618       break;
6619
6620     case 0x16ae:
6621       element = EL_SIGN_WHEELCHAIR;
6622       break;
6623
6624     case 0x16af:
6625       element = EL_SIGN_PARKING;
6626       break;
6627
6628     case 0x16b0:
6629       element = EL_SIGN_NO_ENTRY;
6630       break;
6631
6632     case 0x16b1:
6633       element = EL_SIGN_HEART;
6634       break;
6635
6636     case 0x16b2:
6637       element = EL_SIGN_GIVE_WAY;
6638       break;
6639
6640     case 0x16b3:
6641       element = EL_SIGN_ENTRY_FORBIDDEN;
6642       break;
6643
6644     case 0x16b4:
6645       element = EL_SIGN_EMERGENCY_EXIT;
6646       break;
6647
6648     case 0x16b5:
6649       element = EL_SIGN_YIN_YANG;
6650       break;
6651
6652     case 0x16b6:
6653       element = EL_WALL_EMERALD;
6654       break;
6655
6656     case 0x16b7:
6657       element = EL_WALL_DIAMOND;
6658       break;
6659
6660     case 0x16b8:
6661       element = EL_WALL_PEARL;
6662       break;
6663
6664     case 0x16b9:
6665       element = EL_WALL_CRYSTAL;
6666       break;
6667
6668     case 0x16ba:
6669       element = EL_INVISIBLE_WALL;
6670       break;
6671
6672     case 0x16bb:
6673       element = EL_INVISIBLE_STEELWALL;
6674       break;
6675
6676       // 0x16bc - 0x16cb:
6677       // EL_INVISIBLE_SAND
6678
6679     case 0x16cc:
6680       element = EL_LIGHT_SWITCH;
6681       break;
6682
6683     case 0x16cd:
6684       element = EL_ENVELOPE_1;
6685       break;
6686
6687     default:
6688       if (element >= 0x0117 && element <= 0x036e)       // (?)
6689         element = EL_DIAMOND;
6690       else if (element >= 0x042d && element <= 0x0684)  // (?)
6691         element = EL_EMERALD;
6692       else if (element >= 0x157c && element <= 0x158b)
6693         element = EL_SAND;
6694       else if (element >= 0x1590 && element <= 0x159f)
6695         element = EL_DC_LANDMINE;
6696       else if (element >= 0x16bc && element <= 0x16cb)
6697         element = EL_INVISIBLE_SAND;
6698       else
6699       {
6700         Warn("unknown Diamond Caves element 0x%04x", element);
6701
6702         element = EL_UNKNOWN;
6703       }
6704       break;
6705   }
6706
6707   return getMappedElement(element);
6708 }
6709
6710 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6711 {
6712   byte header[DC_LEVEL_HEADER_SIZE];
6713   int envelope_size;
6714   int envelope_header_pos = 62;
6715   int envelope_content_pos = 94;
6716   int level_name_pos = 251;
6717   int level_author_pos = 292;
6718   int envelope_header_len;
6719   int envelope_content_len;
6720   int level_name_len;
6721   int level_author_len;
6722   int fieldx, fieldy;
6723   int num_yamyam_contents;
6724   int i, x, y;
6725
6726   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6727
6728   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6729   {
6730     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6731
6732     header[i * 2 + 0] = header_word >> 8;
6733     header[i * 2 + 1] = header_word & 0xff;
6734   }
6735
6736   // read some values from level header to check level decoding integrity
6737   fieldx = header[6] | (header[7] << 8);
6738   fieldy = header[8] | (header[9] << 8);
6739   num_yamyam_contents = header[60] | (header[61] << 8);
6740
6741   // do some simple sanity checks to ensure that level was correctly decoded
6742   if (fieldx < 1 || fieldx > 256 ||
6743       fieldy < 1 || fieldy > 256 ||
6744       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6745   {
6746     level->no_valid_file = TRUE;
6747
6748     Warn("cannot decode level from stream -- using empty level");
6749
6750     return;
6751   }
6752
6753   // maximum envelope header size is 31 bytes
6754   envelope_header_len   = header[envelope_header_pos];
6755   // maximum envelope content size is 110 (156?) bytes
6756   envelope_content_len  = header[envelope_content_pos];
6757
6758   // maximum level title size is 40 bytes
6759   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6760   // maximum level author size is 30 (51?) bytes
6761   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6762
6763   envelope_size = 0;
6764
6765   for (i = 0; i < envelope_header_len; i++)
6766     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6767       level->envelope[0].text[envelope_size++] =
6768         header[envelope_header_pos + 1 + i];
6769
6770   if (envelope_header_len > 0 && envelope_content_len > 0)
6771   {
6772     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6773       level->envelope[0].text[envelope_size++] = '\n';
6774     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6775       level->envelope[0].text[envelope_size++] = '\n';
6776   }
6777
6778   for (i = 0; i < envelope_content_len; i++)
6779     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6780       level->envelope[0].text[envelope_size++] =
6781         header[envelope_content_pos + 1 + i];
6782
6783   level->envelope[0].text[envelope_size] = '\0';
6784
6785   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6786   level->envelope[0].ysize = 10;
6787   level->envelope[0].autowrap = TRUE;
6788   level->envelope[0].centered = TRUE;
6789
6790   for (i = 0; i < level_name_len; i++)
6791     level->name[i] = header[level_name_pos + 1 + i];
6792   level->name[level_name_len] = '\0';
6793
6794   for (i = 0; i < level_author_len; i++)
6795     level->author[i] = header[level_author_pos + 1 + i];
6796   level->author[level_author_len] = '\0';
6797
6798   num_yamyam_contents = header[60] | (header[61] << 8);
6799   level->num_yamyam_contents =
6800     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6801
6802   for (i = 0; i < num_yamyam_contents; i++)
6803   {
6804     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6805     {
6806       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6807       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6808
6809       if (i < MAX_ELEMENT_CONTENTS)
6810         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6811     }
6812   }
6813
6814   fieldx = header[6] | (header[7] << 8);
6815   fieldy = header[8] | (header[9] << 8);
6816   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6817   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6818
6819   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6820   {
6821     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6822     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6823
6824     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6825       level->field[x][y] = getMappedElement_DC(element_dc);
6826   }
6827
6828   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6829   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6830   level->field[x][y] = EL_PLAYER_1;
6831
6832   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6833   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6834   level->field[x][y] = EL_PLAYER_2;
6835
6836   level->gems_needed            = header[18] | (header[19] << 8);
6837
6838   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6839   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6840   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6841   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6842   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6843   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6844   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6845   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6846   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6847   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6848   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6849   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6850
6851   level->time                   = header[44] | (header[45] << 8);
6852
6853   level->amoeba_speed           = header[46] | (header[47] << 8);
6854   level->time_light             = header[48] | (header[49] << 8);
6855   level->time_timegate          = header[50] | (header[51] << 8);
6856   level->time_wheel             = header[52] | (header[53] << 8);
6857   level->time_magic_wall        = header[54] | (header[55] << 8);
6858   level->extra_time             = header[56] | (header[57] << 8);
6859   level->shield_normal_time     = header[58] | (header[59] << 8);
6860
6861   // shield and extra time elements do not have a score
6862   level->score[SC_SHIELD]       = 0;
6863   level->extra_time_score       = 0;
6864
6865   // set time for normal and deadly shields to the same value
6866   level->shield_deadly_time     = level->shield_normal_time;
6867
6868   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6869   // can slip down from flat walls, like normal walls and steel walls
6870   level->em_slippery_gems = TRUE;
6871
6872   // time score is counted for each 10 seconds left in Diamond Caves levels
6873   level->time_score_base = 10;
6874 }
6875
6876 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6877                                      struct LevelFileInfo *level_file_info,
6878                                      boolean level_info_only)
6879 {
6880   char *filename = level_file_info->filename;
6881   File *file;
6882   int num_magic_bytes = 8;
6883   char magic_bytes[num_magic_bytes + 1];
6884   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6885
6886   if (!(file = openFile(filename, MODE_READ)))
6887   {
6888     level->no_valid_file = TRUE;
6889
6890     if (!level_info_only)
6891       Warn("cannot read level '%s' -- using empty level", filename);
6892
6893     return;
6894   }
6895
6896   // fseek(file, 0x0000, SEEK_SET);
6897
6898   if (level_file_info->packed)
6899   {
6900     // read "magic bytes" from start of file
6901     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6902       magic_bytes[0] = '\0';
6903
6904     // check "magic bytes" for correct file format
6905     if (!strPrefix(magic_bytes, "DC2"))
6906     {
6907       level->no_valid_file = TRUE;
6908
6909       Warn("unknown DC level file '%s' -- using empty level", filename);
6910
6911       return;
6912     }
6913
6914     if (strPrefix(magic_bytes, "DC2Win95") ||
6915         strPrefix(magic_bytes, "DC2Win98"))
6916     {
6917       int position_first_level = 0x00fa;
6918       int extra_bytes = 4;
6919       int skip_bytes;
6920
6921       // advance file stream to first level inside the level package
6922       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6923
6924       // each block of level data is followed by block of non-level data
6925       num_levels_to_skip *= 2;
6926
6927       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6928       while (num_levels_to_skip >= 0)
6929       {
6930         // advance file stream to next level inside the level package
6931         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6932         {
6933           level->no_valid_file = TRUE;
6934
6935           Warn("cannot fseek in file '%s' -- using empty level", filename);
6936
6937           return;
6938         }
6939
6940         // skip apparently unused extra bytes following each level
6941         ReadUnusedBytesFromFile(file, extra_bytes);
6942
6943         // read size of next level in level package
6944         skip_bytes = getFile32BitLE(file);
6945
6946         num_levels_to_skip--;
6947       }
6948     }
6949     else
6950     {
6951       level->no_valid_file = TRUE;
6952
6953       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6954
6955       return;
6956     }
6957   }
6958
6959   LoadLevelFromFileStream_DC(file, level);
6960
6961   closeFile(file);
6962 }
6963
6964
6965 // ----------------------------------------------------------------------------
6966 // functions for loading SB level
6967 // ----------------------------------------------------------------------------
6968
6969 int getMappedElement_SB(int element_ascii, boolean use_ces)
6970 {
6971   static struct
6972   {
6973     int ascii;
6974     int sb;
6975     int ce;
6976   }
6977   sb_element_mapping[] =
6978   {
6979     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6980     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6981     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6982     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6983     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6984     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6985     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6986     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6987
6988     { 0,   -1,                      -1          },
6989   };
6990
6991   int i;
6992
6993   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6994     if (element_ascii == sb_element_mapping[i].ascii)
6995       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6996
6997   return EL_UNDEFINED;
6998 }
6999
7000 static void SetLevelSettings_SB(struct LevelInfo *level)
7001 {
7002   // time settings
7003   level->time = 0;
7004   level->use_step_counter = TRUE;
7005
7006   // score settings
7007   level->score[SC_TIME_BONUS] = 0;
7008   level->time_score_base = 1;
7009   level->rate_time_over_score = TRUE;
7010
7011   // game settings
7012   level->auto_exit_sokoban = TRUE;
7013 }
7014
7015 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7016                                      struct LevelFileInfo *level_file_info,
7017                                      boolean level_info_only)
7018 {
7019   char *filename = level_file_info->filename;
7020   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7021   char last_comment[MAX_LINE_LEN];
7022   char level_name[MAX_LINE_LEN];
7023   char *line_ptr;
7024   File *file;
7025   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7026   boolean read_continued_line = FALSE;
7027   boolean reading_playfield = FALSE;
7028   boolean got_valid_playfield_line = FALSE;
7029   boolean invalid_playfield_char = FALSE;
7030   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7031   int file_level_nr = 0;
7032   int x = 0, y = 0;             // initialized to make compilers happy
7033
7034   last_comment[0] = '\0';
7035   level_name[0] = '\0';
7036
7037   if (!(file = openFile(filename, MODE_READ)))
7038   {
7039     level->no_valid_file = TRUE;
7040
7041     if (!level_info_only)
7042       Warn("cannot read level '%s' -- using empty level", filename);
7043
7044     return;
7045   }
7046
7047   while (!checkEndOfFile(file))
7048   {
7049     // level successfully read, but next level may follow here
7050     if (!got_valid_playfield_line && reading_playfield)
7051     {
7052       // read playfield from single level file -- skip remaining file
7053       if (!level_file_info->packed)
7054         break;
7055
7056       if (file_level_nr >= num_levels_to_skip)
7057         break;
7058
7059       file_level_nr++;
7060
7061       last_comment[0] = '\0';
7062       level_name[0] = '\0';
7063
7064       reading_playfield = FALSE;
7065     }
7066
7067     got_valid_playfield_line = FALSE;
7068
7069     // read next line of input file
7070     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7071       break;
7072
7073     // cut trailing line break (this can be newline and/or carriage return)
7074     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7075       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7076         *line_ptr = '\0';
7077
7078     // copy raw input line for later use (mainly debugging output)
7079     strcpy(line_raw, line);
7080
7081     if (read_continued_line)
7082     {
7083       // append new line to existing line, if there is enough space
7084       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7085         strcat(previous_line, line_ptr);
7086
7087       strcpy(line, previous_line);      // copy storage buffer to line
7088
7089       read_continued_line = FALSE;
7090     }
7091
7092     // if the last character is '\', continue at next line
7093     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7094     {
7095       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7096       strcpy(previous_line, line);      // copy line to storage buffer
7097
7098       read_continued_line = TRUE;
7099
7100       continue;
7101     }
7102
7103     // skip empty lines
7104     if (line[0] == '\0')
7105       continue;
7106
7107     // extract comment text from comment line
7108     if (line[0] == ';')
7109     {
7110       for (line_ptr = line; *line_ptr; line_ptr++)
7111         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7112           break;
7113
7114       strcpy(last_comment, line_ptr);
7115
7116       continue;
7117     }
7118
7119     // extract level title text from line containing level title
7120     if (line[0] == '\'')
7121     {
7122       strcpy(level_name, &line[1]);
7123
7124       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7125         level_name[strlen(level_name) - 1] = '\0';
7126
7127       continue;
7128     }
7129
7130     // skip lines containing only spaces (or empty lines)
7131     for (line_ptr = line; *line_ptr; line_ptr++)
7132       if (*line_ptr != ' ')
7133         break;
7134     if (*line_ptr == '\0')
7135       continue;
7136
7137     // at this point, we have found a line containing part of a playfield
7138
7139     got_valid_playfield_line = TRUE;
7140
7141     if (!reading_playfield)
7142     {
7143       reading_playfield = TRUE;
7144       invalid_playfield_char = FALSE;
7145
7146       for (x = 0; x < MAX_LEV_FIELDX; x++)
7147         for (y = 0; y < MAX_LEV_FIELDY; y++)
7148           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7149
7150       level->fieldx = 0;
7151       level->fieldy = 0;
7152
7153       // start with topmost tile row
7154       y = 0;
7155     }
7156
7157     // skip playfield line if larger row than allowed
7158     if (y >= MAX_LEV_FIELDY)
7159       continue;
7160
7161     // start with leftmost tile column
7162     x = 0;
7163
7164     // read playfield elements from line
7165     for (line_ptr = line; *line_ptr; line_ptr++)
7166     {
7167       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7168
7169       // stop parsing playfield line if larger column than allowed
7170       if (x >= MAX_LEV_FIELDX)
7171         break;
7172
7173       if (mapped_sb_element == EL_UNDEFINED)
7174       {
7175         invalid_playfield_char = TRUE;
7176
7177         break;
7178       }
7179
7180       level->field[x][y] = mapped_sb_element;
7181
7182       // continue with next tile column
7183       x++;
7184
7185       level->fieldx = MAX(x, level->fieldx);
7186     }
7187
7188     if (invalid_playfield_char)
7189     {
7190       // if first playfield line, treat invalid lines as comment lines
7191       if (y == 0)
7192         reading_playfield = FALSE;
7193
7194       continue;
7195     }
7196
7197     // continue with next tile row
7198     y++;
7199   }
7200
7201   closeFile(file);
7202
7203   level->fieldy = y;
7204
7205   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7206   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7207
7208   if (!reading_playfield)
7209   {
7210     level->no_valid_file = TRUE;
7211
7212     Warn("cannot read level '%s' -- using empty level", filename);
7213
7214     return;
7215   }
7216
7217   if (*level_name != '\0')
7218   {
7219     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7220     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7221   }
7222   else if (*last_comment != '\0')
7223   {
7224     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7225     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7226   }
7227   else
7228   {
7229     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7230   }
7231
7232   // set all empty fields beyond the border walls to invisible steel wall
7233   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7234   {
7235     if ((x == 0 || x == level->fieldx - 1 ||
7236          y == 0 || y == level->fieldy - 1) &&
7237         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7238       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7239                      level->field, level->fieldx, level->fieldy);
7240   }
7241
7242   // set special level settings for Sokoban levels
7243   SetLevelSettings_SB(level);
7244
7245   if (load_xsb_to_ces)
7246   {
7247     // special global settings can now be set in level template
7248     level->use_custom_template = TRUE;
7249   }
7250 }
7251
7252
7253 // -------------------------------------------------------------------------
7254 // functions for handling native levels
7255 // -------------------------------------------------------------------------
7256
7257 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7258                                      struct LevelFileInfo *level_file_info,
7259                                      boolean level_info_only)
7260 {
7261   int pos = 0;
7262
7263   // determine position of requested level inside level package
7264   if (level_file_info->packed)
7265     pos = level_file_info->nr - leveldir_current->first_level;
7266
7267   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7268     level->no_valid_file = TRUE;
7269 }
7270
7271 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7272                                      struct LevelFileInfo *level_file_info,
7273                                      boolean level_info_only)
7274 {
7275   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7276     level->no_valid_file = TRUE;
7277 }
7278
7279 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7280                                      struct LevelFileInfo *level_file_info,
7281                                      boolean level_info_only)
7282 {
7283   int pos = 0;
7284
7285   // determine position of requested level inside level package
7286   if (level_file_info->packed)
7287     pos = level_file_info->nr - leveldir_current->first_level;
7288
7289   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7290     level->no_valid_file = TRUE;
7291 }
7292
7293 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7294                                      struct LevelFileInfo *level_file_info,
7295                                      boolean level_info_only)
7296 {
7297   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7298     level->no_valid_file = TRUE;
7299 }
7300
7301 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7302 {
7303   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7304     CopyNativeLevel_RND_to_BD(level);
7305   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7306     CopyNativeLevel_RND_to_EM(level);
7307   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7308     CopyNativeLevel_RND_to_SP(level);
7309   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7310     CopyNativeLevel_RND_to_MM(level);
7311 }
7312
7313 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7314 {
7315   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7316     CopyNativeLevel_BD_to_RND(level);
7317   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7318     CopyNativeLevel_EM_to_RND(level);
7319   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7320     CopyNativeLevel_SP_to_RND(level);
7321   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7322     CopyNativeLevel_MM_to_RND(level);
7323 }
7324
7325 void SaveNativeLevel(struct LevelInfo *level)
7326 {
7327   // saving native level files only supported for some game engines
7328   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7329       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7330     return;
7331
7332   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7333                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7334   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7335   char *filename = getLevelFilenameFromBasename(basename);
7336
7337   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7338     return;
7339
7340   boolean success = FALSE;
7341
7342   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7343   {
7344     CopyNativeLevel_RND_to_BD(level);
7345     // CopyNativeTape_RND_to_BD(level);
7346
7347     success = SaveNativeLevel_BD(filename);
7348   }
7349   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7350   {
7351     CopyNativeLevel_RND_to_SP(level);
7352     CopyNativeTape_RND_to_SP(level);
7353
7354     success = SaveNativeLevel_SP(filename);
7355   }
7356
7357   if (success)
7358     Request("Native level file saved!", REQ_CONFIRM);
7359   else
7360     Request("Failed to save native level file!", REQ_CONFIRM);
7361 }
7362
7363
7364 // ----------------------------------------------------------------------------
7365 // functions for loading generic level
7366 // ----------------------------------------------------------------------------
7367
7368 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7369                                   struct LevelFileInfo *level_file_info,
7370                                   boolean level_info_only)
7371 {
7372   // always start with reliable default values
7373   setLevelInfoToDefaults(level, level_info_only, TRUE);
7374
7375   switch (level_file_info->type)
7376   {
7377     case LEVEL_FILE_TYPE_RND:
7378       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7379       break;
7380
7381     case LEVEL_FILE_TYPE_BD:
7382       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7383       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7384       break;
7385
7386     case LEVEL_FILE_TYPE_EM:
7387       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7388       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7389       break;
7390
7391     case LEVEL_FILE_TYPE_SP:
7392       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7393       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7394       break;
7395
7396     case LEVEL_FILE_TYPE_MM:
7397       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7398       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7399       break;
7400
7401     case LEVEL_FILE_TYPE_DC:
7402       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7403       break;
7404
7405     case LEVEL_FILE_TYPE_SB:
7406       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7407       break;
7408
7409     default:
7410       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7411       break;
7412   }
7413
7414   // if level file is invalid, restore level structure to default values
7415   if (level->no_valid_file)
7416     setLevelInfoToDefaults(level, level_info_only, FALSE);
7417
7418   if (check_special_flags("use_native_bd_game_engine"))
7419     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7420
7421   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7422     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7423
7424   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7425     CopyNativeLevel_Native_to_RND(level);
7426 }
7427
7428 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7429 {
7430   static struct LevelFileInfo level_file_info;
7431
7432   // always start with reliable default values
7433   setFileInfoToDefaults(&level_file_info);
7434
7435   level_file_info.nr = 0;                       // unknown level number
7436   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7437
7438   setString(&level_file_info.filename, filename);
7439
7440   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7441 }
7442
7443 static void LoadLevel_InitVersion(struct LevelInfo *level)
7444 {
7445   int i, j;
7446
7447   if (leveldir_current == NULL)         // only when dumping level
7448     return;
7449
7450   // all engine modifications also valid for levels which use latest engine
7451   if (level->game_version < VERSION_IDENT(3,2,0,5))
7452   {
7453     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7454     level->time_score_base = 10;
7455   }
7456
7457   if (leveldir_current->latest_engine)
7458   {
7459     // ---------- use latest game engine --------------------------------------
7460
7461     /* For all levels which are forced to use the latest game engine version
7462        (normally all but user contributed, private and undefined levels), set
7463        the game engine version to the actual version; this allows for actual
7464        corrections in the game engine to take effect for existing, converted
7465        levels (from "classic" or other existing games) to make the emulation
7466        of the corresponding game more accurate, while (hopefully) not breaking
7467        existing levels created from other players. */
7468
7469     level->game_version = GAME_VERSION_ACTUAL;
7470
7471     /* Set special EM style gems behaviour: EM style gems slip down from
7472        normal, steel and growing wall. As this is a more fundamental change,
7473        it seems better to set the default behaviour to "off" (as it is more
7474        natural) and make it configurable in the level editor (as a property
7475        of gem style elements). Already existing converted levels (neither
7476        private nor contributed levels) are changed to the new behaviour. */
7477
7478     if (level->file_version < FILE_VERSION_2_0)
7479       level->em_slippery_gems = TRUE;
7480
7481     return;
7482   }
7483
7484   // ---------- use game engine the level was created with --------------------
7485
7486   /* For all levels which are not forced to use the latest game engine
7487      version (normally user contributed, private and undefined levels),
7488      use the version of the game engine the levels were created for.
7489
7490      Since 2.0.1, the game engine version is now directly stored
7491      in the level file (chunk "VERS"), so there is no need anymore
7492      to set the game version from the file version (except for old,
7493      pre-2.0 levels, where the game version is still taken from the
7494      file format version used to store the level -- see above). */
7495
7496   // player was faster than enemies in 1.0.0 and before
7497   if (level->file_version == FILE_VERSION_1_0)
7498     for (i = 0; i < MAX_PLAYERS; i++)
7499       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7500
7501   // default behaviour for EM style gems was "slippery" only in 2.0.1
7502   if (level->game_version == VERSION_IDENT(2,0,1,0))
7503     level->em_slippery_gems = TRUE;
7504
7505   // springs could be pushed over pits before (pre-release version) 2.2.0
7506   if (level->game_version < VERSION_IDENT(2,2,0,0))
7507     level->use_spring_bug = TRUE;
7508
7509   if (level->game_version < VERSION_IDENT(3,2,0,5))
7510   {
7511     // time orb caused limited time in endless time levels before 3.2.0-5
7512     level->use_time_orb_bug = TRUE;
7513
7514     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7515     level->block_snap_field = FALSE;
7516
7517     // extra time score was same value as time left score before 3.2.0-5
7518     level->extra_time_score = level->score[SC_TIME_BONUS];
7519   }
7520
7521   if (level->game_version < VERSION_IDENT(3,2,0,7))
7522   {
7523     // default behaviour for snapping was "not continuous" before 3.2.0-7
7524     level->continuous_snapping = FALSE;
7525   }
7526
7527   // only few elements were able to actively move into acid before 3.1.0
7528   // trigger settings did not exist before 3.1.0; set to default "any"
7529   if (level->game_version < VERSION_IDENT(3,1,0,0))
7530   {
7531     // correct "can move into acid" settings (all zero in old levels)
7532
7533     level->can_move_into_acid_bits = 0; // nothing can move into acid
7534     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7535
7536     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7537     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7538     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7539     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7540
7541     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7542       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7543
7544     // correct trigger settings (stored as zero == "none" in old levels)
7545
7546     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7547     {
7548       int element = EL_CUSTOM_START + i;
7549       struct ElementInfo *ei = &element_info[element];
7550
7551       for (j = 0; j < ei->num_change_pages; j++)
7552       {
7553         struct ElementChangeInfo *change = &ei->change_page[j];
7554
7555         change->trigger_player = CH_PLAYER_ANY;
7556         change->trigger_page = CH_PAGE_ANY;
7557       }
7558     }
7559   }
7560
7561   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7562   {
7563     int element = EL_CUSTOM_256;
7564     struct ElementInfo *ei = &element_info[element];
7565     struct ElementChangeInfo *change = &ei->change_page[0];
7566
7567     /* This is needed to fix a problem that was caused by a bugfix in function
7568        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7569        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7570        not replace walkable elements, but instead just placed the player on it,
7571        without placing the Sokoban field under the player). Unfortunately, this
7572        breaks "Snake Bite" style levels when the snake is halfway through a door
7573        that just closes (the snake head is still alive and can be moved in this
7574        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7575        player (without Sokoban element) which then gets killed as designed). */
7576
7577     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7578          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7579         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7580       change->target_element = EL_PLAYER_1;
7581   }
7582
7583   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7584   if (level->game_version < VERSION_IDENT(3,2,5,0))
7585   {
7586     /* This is needed to fix a problem that was caused by a bugfix in function
7587        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7588        corrects the behaviour when a custom element changes to another custom
7589        element with a higher element number that has change actions defined.
7590        Normally, only one change per frame is allowed for custom elements.
7591        Therefore, it is checked if a custom element already changed in the
7592        current frame; if it did, subsequent changes are suppressed.
7593        Unfortunately, this is only checked for element changes, but not for
7594        change actions, which are still executed. As the function above loops
7595        through all custom elements from lower to higher, an element change
7596        resulting in a lower CE number won't be checked again, while a target
7597        element with a higher number will also be checked, and potential change
7598        actions will get executed for this CE, too (which is wrong), while
7599        further changes are ignored (which is correct). As this bugfix breaks
7600        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7601        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7602        behaviour for existing levels and tapes that make use of this bug */
7603
7604     level->use_action_after_change_bug = TRUE;
7605   }
7606
7607   // not centering level after relocating player was default only in 3.2.3
7608   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7609     level->shifted_relocation = TRUE;
7610
7611   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7612   if (level->game_version < VERSION_IDENT(3,2,6,0))
7613     level->em_explodes_by_fire = TRUE;
7614
7615   // levels were solved by the first player entering an exit up to 4.1.0.0
7616   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7617     level->solved_by_one_player = TRUE;
7618
7619   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7620   if (level->game_version < VERSION_IDENT(4,1,1,1))
7621     level->use_life_bugs = TRUE;
7622
7623   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7624   if (level->game_version < VERSION_IDENT(4,1,1,1))
7625     level->sb_objects_needed = FALSE;
7626
7627   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7628   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7629     level->finish_dig_collect = FALSE;
7630
7631   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7632   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7633     level->keep_walkable_ce = TRUE;
7634 }
7635
7636 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7637 {
7638   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7639   int x, y;
7640
7641   // check if this level is (not) a Sokoban level
7642   for (y = 0; y < level->fieldy; y++)
7643     for (x = 0; x < level->fieldx; x++)
7644       if (!IS_SB_ELEMENT(Tile[x][y]))
7645         is_sokoban_level = FALSE;
7646
7647   if (is_sokoban_level)
7648   {
7649     // set special level settings for Sokoban levels
7650     SetLevelSettings_SB(level);
7651   }
7652 }
7653
7654 static void LoadLevel_InitSettings(struct LevelInfo *level)
7655 {
7656   // adjust level settings for (non-native) Sokoban-style levels
7657   LoadLevel_InitSettings_SB(level);
7658
7659   // rename levels with title "nameless level" or if renaming is forced
7660   if (leveldir_current->empty_level_name != NULL &&
7661       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7662        leveldir_current->force_level_name))
7663     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7664              leveldir_current->empty_level_name, level_nr);
7665 }
7666
7667 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7668 {
7669   int i, x, y;
7670
7671   // map elements that have changed in newer versions
7672   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7673                                                     level->game_version);
7674   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7675     for (x = 0; x < 3; x++)
7676       for (y = 0; y < 3; y++)
7677         level->yamyam_content[i].e[x][y] =
7678           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7679                                     level->game_version);
7680
7681 }
7682
7683 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7684 {
7685   int i, j;
7686
7687   // map custom element change events that have changed in newer versions
7688   // (these following values were accidentally changed in version 3.0.1)
7689   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7690   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7691   {
7692     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7693     {
7694       int element = EL_CUSTOM_START + i;
7695
7696       // order of checking and copying events to be mapped is important
7697       // (do not change the start and end value -- they are constant)
7698       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7699       {
7700         if (HAS_CHANGE_EVENT(element, j - 2))
7701         {
7702           SET_CHANGE_EVENT(element, j - 2, FALSE);
7703           SET_CHANGE_EVENT(element, j, TRUE);
7704         }
7705       }
7706
7707       // order of checking and copying events to be mapped is important
7708       // (do not change the start and end value -- they are constant)
7709       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7710       {
7711         if (HAS_CHANGE_EVENT(element, j - 1))
7712         {
7713           SET_CHANGE_EVENT(element, j - 1, FALSE);
7714           SET_CHANGE_EVENT(element, j, TRUE);
7715         }
7716       }
7717     }
7718   }
7719
7720   // initialize "can_change" field for old levels with only one change page
7721   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7722   {
7723     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7724     {
7725       int element = EL_CUSTOM_START + i;
7726
7727       if (CAN_CHANGE(element))
7728         element_info[element].change->can_change = TRUE;
7729     }
7730   }
7731
7732   // correct custom element values (for old levels without these options)
7733   if (level->game_version < VERSION_IDENT(3,1,1,0))
7734   {
7735     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7736     {
7737       int element = EL_CUSTOM_START + i;
7738       struct ElementInfo *ei = &element_info[element];
7739
7740       if (ei->access_direction == MV_NO_DIRECTION)
7741         ei->access_direction = MV_ALL_DIRECTIONS;
7742     }
7743   }
7744
7745   // correct custom element values (fix invalid values for all versions)
7746   if (1)
7747   {
7748     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7749     {
7750       int element = EL_CUSTOM_START + i;
7751       struct ElementInfo *ei = &element_info[element];
7752
7753       for (j = 0; j < ei->num_change_pages; j++)
7754       {
7755         struct ElementChangeInfo *change = &ei->change_page[j];
7756
7757         if (change->trigger_player == CH_PLAYER_NONE)
7758           change->trigger_player = CH_PLAYER_ANY;
7759
7760         if (change->trigger_side == CH_SIDE_NONE)
7761           change->trigger_side = CH_SIDE_ANY;
7762       }
7763     }
7764   }
7765
7766   // initialize "can_explode" field for old levels which did not store this
7767   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7768   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7769   {
7770     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7771     {
7772       int element = EL_CUSTOM_START + i;
7773
7774       if (EXPLODES_1X1_OLD(element))
7775         element_info[element].explosion_type = EXPLODES_1X1;
7776
7777       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7778                                              EXPLODES_SMASHED(element) ||
7779                                              EXPLODES_IMPACT(element)));
7780     }
7781   }
7782
7783   // correct previously hard-coded move delay values for maze runner style
7784   if (level->game_version < VERSION_IDENT(3,1,1,0))
7785   {
7786     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7787     {
7788       int element = EL_CUSTOM_START + i;
7789
7790       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7791       {
7792         // previously hard-coded and therefore ignored
7793         element_info[element].move_delay_fixed = 9;
7794         element_info[element].move_delay_random = 0;
7795       }
7796     }
7797   }
7798
7799   // set some other uninitialized values of custom elements in older levels
7800   if (level->game_version < VERSION_IDENT(3,1,0,0))
7801   {
7802     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7803     {
7804       int element = EL_CUSTOM_START + i;
7805
7806       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7807
7808       element_info[element].explosion_delay = 17;
7809       element_info[element].ignition_delay = 8;
7810     }
7811   }
7812
7813   // set mouse click change events to work for left/middle/right mouse button
7814   if (level->game_version < VERSION_IDENT(4,2,3,0))
7815   {
7816     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7817     {
7818       int element = EL_CUSTOM_START + i;
7819       struct ElementInfo *ei = &element_info[element];
7820
7821       for (j = 0; j < ei->num_change_pages; j++)
7822       {
7823         struct ElementChangeInfo *change = &ei->change_page[j];
7824
7825         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7826             change->has_event[CE_PRESSED_BY_MOUSE] ||
7827             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7828             change->has_event[CE_MOUSE_PRESSED_ON_X])
7829           change->trigger_side = CH_SIDE_ANY;
7830       }
7831     }
7832   }
7833 }
7834
7835 static void LoadLevel_InitElements(struct LevelInfo *level)
7836 {
7837   LoadLevel_InitStandardElements(level);
7838
7839   if (level->file_has_custom_elements)
7840     LoadLevel_InitCustomElements(level);
7841
7842   // initialize element properties for level editor etc.
7843   InitElementPropertiesEngine(level->game_version);
7844   InitElementPropertiesGfxElement();
7845 }
7846
7847 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7848 {
7849   int x, y;
7850
7851   // map elements that have changed in newer versions
7852   for (y = 0; y < level->fieldy; y++)
7853     for (x = 0; x < level->fieldx; x++)
7854       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7855                                                      level->game_version);
7856
7857   // clear unused playfield data (nicer if level gets resized in editor)
7858   for (x = 0; x < MAX_LEV_FIELDX; x++)
7859     for (y = 0; y < MAX_LEV_FIELDY; y++)
7860       if (x >= level->fieldx || y >= level->fieldy)
7861         level->field[x][y] = EL_EMPTY;
7862
7863   // copy elements to runtime playfield array
7864   for (x = 0; x < MAX_LEV_FIELDX; x++)
7865     for (y = 0; y < MAX_LEV_FIELDY; y++)
7866       Tile[x][y] = level->field[x][y];
7867
7868   // initialize level size variables for faster access
7869   lev_fieldx = level->fieldx;
7870   lev_fieldy = level->fieldy;
7871
7872   // determine border element for this level
7873   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7874     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7875   else
7876     SetBorderElement();
7877 }
7878
7879 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7880 {
7881   struct LevelFileInfo *level_file_info = &level->file_info;
7882
7883   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7884     CopyNativeLevel_RND_to_Native(level);
7885 }
7886
7887 static void LoadLevelTemplate_LoadAndInit(void)
7888 {
7889   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7890
7891   LoadLevel_InitVersion(&level_template);
7892   LoadLevel_InitElements(&level_template);
7893   LoadLevel_InitSettings(&level_template);
7894
7895   ActivateLevelTemplate();
7896 }
7897
7898 void LoadLevelTemplate(int nr)
7899 {
7900   if (!fileExists(getGlobalLevelTemplateFilename()))
7901   {
7902     Warn("no level template found for this level");
7903
7904     return;
7905   }
7906
7907   setLevelFileInfo(&level_template.file_info, nr);
7908
7909   LoadLevelTemplate_LoadAndInit();
7910 }
7911
7912 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7913 {
7914   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7915
7916   LoadLevelTemplate_LoadAndInit();
7917 }
7918
7919 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7920 {
7921   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7922
7923   if (level.use_custom_template)
7924   {
7925     if (network_level != NULL)
7926       LoadNetworkLevelTemplate(network_level);
7927     else
7928       LoadLevelTemplate(-1);
7929   }
7930
7931   LoadLevel_InitVersion(&level);
7932   LoadLevel_InitElements(&level);
7933   LoadLevel_InitPlayfield(&level);
7934   LoadLevel_InitSettings(&level);
7935
7936   LoadLevel_InitNativeEngines(&level);
7937 }
7938
7939 void LoadLevel(int nr)
7940 {
7941   SetLevelSetInfo(leveldir_current->identifier, nr);
7942
7943   setLevelFileInfo(&level.file_info, nr);
7944
7945   LoadLevel_LoadAndInit(NULL);
7946 }
7947
7948 void LoadLevelInfoOnly(int nr)
7949 {
7950   setLevelFileInfo(&level.file_info, nr);
7951
7952   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7953 }
7954
7955 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7956 {
7957   SetLevelSetInfo(network_level->leveldir_identifier,
7958                   network_level->file_info.nr);
7959
7960   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7961
7962   LoadLevel_LoadAndInit(network_level);
7963 }
7964
7965 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7966 {
7967   int chunk_size = 0;
7968
7969   chunk_size += putFileVersion(file, level->file_version);
7970   chunk_size += putFileVersion(file, level->game_version);
7971
7972   return chunk_size;
7973 }
7974
7975 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7976 {
7977   int chunk_size = 0;
7978
7979   chunk_size += putFile16BitBE(file, level->creation_date.year);
7980   chunk_size += putFile8Bit(file,    level->creation_date.month);
7981   chunk_size += putFile8Bit(file,    level->creation_date.day);
7982
7983   return chunk_size;
7984 }
7985
7986 #if ENABLE_HISTORIC_CHUNKS
7987 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7988 {
7989   int i, x, y;
7990
7991   putFile8Bit(file, level->fieldx);
7992   putFile8Bit(file, level->fieldy);
7993
7994   putFile16BitBE(file, level->time);
7995   putFile16BitBE(file, level->gems_needed);
7996
7997   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7998     putFile8Bit(file, level->name[i]);
7999
8000   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8001     putFile8Bit(file, level->score[i]);
8002
8003   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8004     for (y = 0; y < 3; y++)
8005       for (x = 0; x < 3; x++)
8006         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8007                            level->yamyam_content[i].e[x][y]));
8008   putFile8Bit(file, level->amoeba_speed);
8009   putFile8Bit(file, level->time_magic_wall);
8010   putFile8Bit(file, level->time_wheel);
8011   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8012                      level->amoeba_content));
8013   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8014   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8015   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8016   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8017
8018   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8019
8020   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8021   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8022   putFile32BitBE(file, level->can_move_into_acid_bits);
8023   putFile8Bit(file, level->dont_collide_with_bits);
8024
8025   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8026   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8027
8028   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8029   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8030   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8031
8032   putFile8Bit(file, level->game_engine_type);
8033
8034   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8035 }
8036 #endif
8037
8038 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8039 {
8040   int chunk_size = 0;
8041   int i;
8042
8043   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8044     chunk_size += putFile8Bit(file, level->name[i]);
8045
8046   return chunk_size;
8047 }
8048
8049 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8050 {
8051   int chunk_size = 0;
8052   int i;
8053
8054   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8055     chunk_size += putFile8Bit(file, level->author[i]);
8056
8057   return chunk_size;
8058 }
8059
8060 #if ENABLE_HISTORIC_CHUNKS
8061 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8062 {
8063   int chunk_size = 0;
8064   int x, y;
8065
8066   for (y = 0; y < level->fieldy; y++)
8067     for (x = 0; x < level->fieldx; x++)
8068       if (level->encoding_16bit_field)
8069         chunk_size += putFile16BitBE(file, level->field[x][y]);
8070       else
8071         chunk_size += putFile8Bit(file, level->field[x][y]);
8072
8073   return chunk_size;
8074 }
8075 #endif
8076
8077 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8078 {
8079   int chunk_size = 0;
8080   int x, y;
8081
8082   for (y = 0; y < level->fieldy; y++) 
8083     for (x = 0; x < level->fieldx; x++) 
8084       chunk_size += putFile16BitBE(file, level->field[x][y]);
8085
8086   return chunk_size;
8087 }
8088
8089 #if ENABLE_HISTORIC_CHUNKS
8090 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8091 {
8092   int i, x, y;
8093
8094   putFile8Bit(file, EL_YAMYAM);
8095   putFile8Bit(file, level->num_yamyam_contents);
8096   putFile8Bit(file, 0);
8097   putFile8Bit(file, 0);
8098
8099   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8100     for (y = 0; y < 3; y++)
8101       for (x = 0; x < 3; x++)
8102         if (level->encoding_16bit_field)
8103           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8104         else
8105           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8106 }
8107 #endif
8108
8109 #if ENABLE_HISTORIC_CHUNKS
8110 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8111 {
8112   int i, x, y;
8113   int num_contents, content_xsize, content_ysize;
8114   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8115
8116   if (element == EL_YAMYAM)
8117   {
8118     num_contents = level->num_yamyam_contents;
8119     content_xsize = 3;
8120     content_ysize = 3;
8121
8122     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8123       for (y = 0; y < 3; y++)
8124         for (x = 0; x < 3; x++)
8125           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8126   }
8127   else if (element == EL_BD_AMOEBA)
8128   {
8129     num_contents = 1;
8130     content_xsize = 1;
8131     content_ysize = 1;
8132
8133     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8134       for (y = 0; y < 3; y++)
8135         for (x = 0; x < 3; x++)
8136           content_array[i][x][y] = EL_EMPTY;
8137     content_array[0][0][0] = level->amoeba_content;
8138   }
8139   else
8140   {
8141     // chunk header already written -- write empty chunk data
8142     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8143
8144     Warn("cannot save content for element '%d'", element);
8145
8146     return;
8147   }
8148
8149   putFile16BitBE(file, element);
8150   putFile8Bit(file, num_contents);
8151   putFile8Bit(file, content_xsize);
8152   putFile8Bit(file, content_ysize);
8153
8154   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8155
8156   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8157     for (y = 0; y < 3; y++)
8158       for (x = 0; x < 3; x++)
8159         putFile16BitBE(file, content_array[i][x][y]);
8160 }
8161 #endif
8162
8163 #if ENABLE_HISTORIC_CHUNKS
8164 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8165 {
8166   int envelope_nr = element - EL_ENVELOPE_1;
8167   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8168   int chunk_size = 0;
8169   int i;
8170
8171   chunk_size += putFile16BitBE(file, element);
8172   chunk_size += putFile16BitBE(file, envelope_len);
8173   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8174   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8175
8176   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8177   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8178
8179   for (i = 0; i < envelope_len; i++)
8180     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8181
8182   return chunk_size;
8183 }
8184 #endif
8185
8186 #if ENABLE_HISTORIC_CHUNKS
8187 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8188                            int num_changed_custom_elements)
8189 {
8190   int i, check = 0;
8191
8192   putFile16BitBE(file, num_changed_custom_elements);
8193
8194   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8195   {
8196     int element = EL_CUSTOM_START + i;
8197
8198     struct ElementInfo *ei = &element_info[element];
8199
8200     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8201     {
8202       if (check < num_changed_custom_elements)
8203       {
8204         putFile16BitBE(file, element);
8205         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8206       }
8207
8208       check++;
8209     }
8210   }
8211
8212   if (check != num_changed_custom_elements)     // should not happen
8213     Warn("inconsistent number of custom element properties");
8214 }
8215 #endif
8216
8217 #if ENABLE_HISTORIC_CHUNKS
8218 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8219                            int num_changed_custom_elements)
8220 {
8221   int i, check = 0;
8222
8223   putFile16BitBE(file, num_changed_custom_elements);
8224
8225   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8226   {
8227     int element = EL_CUSTOM_START + i;
8228
8229     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8230     {
8231       if (check < num_changed_custom_elements)
8232       {
8233         putFile16BitBE(file, element);
8234         putFile16BitBE(file, element_info[element].change->target_element);
8235       }
8236
8237       check++;
8238     }
8239   }
8240
8241   if (check != num_changed_custom_elements)     // should not happen
8242     Warn("inconsistent number of custom target elements");
8243 }
8244 #endif
8245
8246 #if ENABLE_HISTORIC_CHUNKS
8247 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8248                            int num_changed_custom_elements)
8249 {
8250   int i, j, x, y, check = 0;
8251
8252   putFile16BitBE(file, num_changed_custom_elements);
8253
8254   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8255   {
8256     int element = EL_CUSTOM_START + i;
8257     struct ElementInfo *ei = &element_info[element];
8258
8259     if (ei->modified_settings)
8260     {
8261       if (check < num_changed_custom_elements)
8262       {
8263         putFile16BitBE(file, element);
8264
8265         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8266           putFile8Bit(file, ei->description[j]);
8267
8268         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8269
8270         // some free bytes for future properties and padding
8271         WriteUnusedBytesToFile(file, 7);
8272
8273         putFile8Bit(file, ei->use_gfx_element);
8274         putFile16BitBE(file, ei->gfx_element_initial);
8275
8276         putFile8Bit(file, ei->collect_score_initial);
8277         putFile8Bit(file, ei->collect_count_initial);
8278
8279         putFile16BitBE(file, ei->push_delay_fixed);
8280         putFile16BitBE(file, ei->push_delay_random);
8281         putFile16BitBE(file, ei->move_delay_fixed);
8282         putFile16BitBE(file, ei->move_delay_random);
8283
8284         putFile16BitBE(file, ei->move_pattern);
8285         putFile8Bit(file, ei->move_direction_initial);
8286         putFile8Bit(file, ei->move_stepsize);
8287
8288         for (y = 0; y < 3; y++)
8289           for (x = 0; x < 3; x++)
8290             putFile16BitBE(file, ei->content.e[x][y]);
8291
8292         putFile32BitBE(file, ei->change->events);
8293
8294         putFile16BitBE(file, ei->change->target_element);
8295
8296         putFile16BitBE(file, ei->change->delay_fixed);
8297         putFile16BitBE(file, ei->change->delay_random);
8298         putFile16BitBE(file, ei->change->delay_frames);
8299
8300         putFile16BitBE(file, ei->change->initial_trigger_element);
8301
8302         putFile8Bit(file, ei->change->explode);
8303         putFile8Bit(file, ei->change->use_target_content);
8304         putFile8Bit(file, ei->change->only_if_complete);
8305         putFile8Bit(file, ei->change->use_random_replace);
8306
8307         putFile8Bit(file, ei->change->random_percentage);
8308         putFile8Bit(file, ei->change->replace_when);
8309
8310         for (y = 0; y < 3; y++)
8311           for (x = 0; x < 3; x++)
8312             putFile16BitBE(file, ei->change->content.e[x][y]);
8313
8314         putFile8Bit(file, ei->slippery_type);
8315
8316         // some free bytes for future properties and padding
8317         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8318       }
8319
8320       check++;
8321     }
8322   }
8323
8324   if (check != num_changed_custom_elements)     // should not happen
8325     Warn("inconsistent number of custom element properties");
8326 }
8327 #endif
8328
8329 #if ENABLE_HISTORIC_CHUNKS
8330 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8331 {
8332   struct ElementInfo *ei = &element_info[element];
8333   int i, j, x, y;
8334
8335   // ---------- custom element base property values (96 bytes) ----------------
8336
8337   putFile16BitBE(file, element);
8338
8339   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8340     putFile8Bit(file, ei->description[i]);
8341
8342   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8343
8344   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8345
8346   putFile8Bit(file, ei->num_change_pages);
8347
8348   putFile16BitBE(file, ei->ce_value_fixed_initial);
8349   putFile16BitBE(file, ei->ce_value_random_initial);
8350   putFile8Bit(file, ei->use_last_ce_value);
8351
8352   putFile8Bit(file, ei->use_gfx_element);
8353   putFile16BitBE(file, ei->gfx_element_initial);
8354
8355   putFile8Bit(file, ei->collect_score_initial);
8356   putFile8Bit(file, ei->collect_count_initial);
8357
8358   putFile8Bit(file, ei->drop_delay_fixed);
8359   putFile8Bit(file, ei->push_delay_fixed);
8360   putFile8Bit(file, ei->drop_delay_random);
8361   putFile8Bit(file, ei->push_delay_random);
8362   putFile16BitBE(file, ei->move_delay_fixed);
8363   putFile16BitBE(file, ei->move_delay_random);
8364
8365   // bits 0 - 15 of "move_pattern" ...
8366   putFile16BitBE(file, ei->move_pattern & 0xffff);
8367   putFile8Bit(file, ei->move_direction_initial);
8368   putFile8Bit(file, ei->move_stepsize);
8369
8370   putFile8Bit(file, ei->slippery_type);
8371
8372   for (y = 0; y < 3; y++)
8373     for (x = 0; x < 3; x++)
8374       putFile16BitBE(file, ei->content.e[x][y]);
8375
8376   putFile16BitBE(file, ei->move_enter_element);
8377   putFile16BitBE(file, ei->move_leave_element);
8378   putFile8Bit(file, ei->move_leave_type);
8379
8380   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8381   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8382
8383   putFile8Bit(file, ei->access_direction);
8384
8385   putFile8Bit(file, ei->explosion_delay);
8386   putFile8Bit(file, ei->ignition_delay);
8387   putFile8Bit(file, ei->explosion_type);
8388
8389   // some free bytes for future custom property values and padding
8390   WriteUnusedBytesToFile(file, 1);
8391
8392   // ---------- change page property values (48 bytes) ------------------------
8393
8394   for (i = 0; i < ei->num_change_pages; i++)
8395   {
8396     struct ElementChangeInfo *change = &ei->change_page[i];
8397     unsigned int event_bits;
8398
8399     // bits 0 - 31 of "has_event[]" ...
8400     event_bits = 0;
8401     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8402       if (change->has_event[j])
8403         event_bits |= (1u << j);
8404     putFile32BitBE(file, event_bits);
8405
8406     putFile16BitBE(file, change->target_element);
8407
8408     putFile16BitBE(file, change->delay_fixed);
8409     putFile16BitBE(file, change->delay_random);
8410     putFile16BitBE(file, change->delay_frames);
8411
8412     putFile16BitBE(file, change->initial_trigger_element);
8413
8414     putFile8Bit(file, change->explode);
8415     putFile8Bit(file, change->use_target_content);
8416     putFile8Bit(file, change->only_if_complete);
8417     putFile8Bit(file, change->use_random_replace);
8418
8419     putFile8Bit(file, change->random_percentage);
8420     putFile8Bit(file, change->replace_when);
8421
8422     for (y = 0; y < 3; y++)
8423       for (x = 0; x < 3; x++)
8424         putFile16BitBE(file, change->target_content.e[x][y]);
8425
8426     putFile8Bit(file, change->can_change);
8427
8428     putFile8Bit(file, change->trigger_side);
8429
8430     putFile8Bit(file, change->trigger_player);
8431     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8432                        log_2(change->trigger_page)));
8433
8434     putFile8Bit(file, change->has_action);
8435     putFile8Bit(file, change->action_type);
8436     putFile8Bit(file, change->action_mode);
8437     putFile16BitBE(file, change->action_arg);
8438
8439     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8440     event_bits = 0;
8441     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8442       if (change->has_event[j])
8443         event_bits |= (1u << (j - 32));
8444     putFile8Bit(file, event_bits);
8445   }
8446 }
8447 #endif
8448
8449 #if ENABLE_HISTORIC_CHUNKS
8450 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8451 {
8452   struct ElementInfo *ei = &element_info[element];
8453   struct ElementGroupInfo *group = ei->group;
8454   int i;
8455
8456   putFile16BitBE(file, element);
8457
8458   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8459     putFile8Bit(file, ei->description[i]);
8460
8461   putFile8Bit(file, group->num_elements);
8462
8463   putFile8Bit(file, ei->use_gfx_element);
8464   putFile16BitBE(file, ei->gfx_element_initial);
8465
8466   putFile8Bit(file, group->choice_mode);
8467
8468   // some free bytes for future values and padding
8469   WriteUnusedBytesToFile(file, 3);
8470
8471   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8472     putFile16BitBE(file, group->element[i]);
8473 }
8474 #endif
8475
8476 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8477                                 boolean write_element)
8478 {
8479   int save_type = entry->save_type;
8480   int data_type = entry->data_type;
8481   int conf_type = entry->conf_type;
8482   int byte_mask = conf_type & CONF_MASK_BYTES;
8483   int element = entry->element;
8484   int default_value = entry->default_value;
8485   int num_bytes = 0;
8486   boolean modified = FALSE;
8487
8488   if (byte_mask != CONF_MASK_MULTI_BYTES)
8489   {
8490     void *value_ptr = entry->value;
8491     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8492                  *(int *)value_ptr);
8493
8494     // check if any settings have been modified before saving them
8495     if (value != default_value)
8496       modified = TRUE;
8497
8498     // do not save if explicitly told or if unmodified default settings
8499     if ((save_type == SAVE_CONF_NEVER) ||
8500         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8501       return 0;
8502
8503     if (write_element)
8504       num_bytes += putFile16BitBE(file, element);
8505
8506     num_bytes += putFile8Bit(file, conf_type);
8507     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8508                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8509                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8510                   0);
8511   }
8512   else if (data_type == TYPE_STRING)
8513   {
8514     char *default_string = entry->default_string;
8515     char *string = (char *)(entry->value);
8516     int string_length = strlen(string);
8517     int i;
8518
8519     // check if any settings have been modified before saving them
8520     if (!strEqual(string, default_string))
8521       modified = TRUE;
8522
8523     // do not save if explicitly told or if unmodified default settings
8524     if ((save_type == SAVE_CONF_NEVER) ||
8525         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8526       return 0;
8527
8528     if (write_element)
8529       num_bytes += putFile16BitBE(file, element);
8530
8531     num_bytes += putFile8Bit(file, conf_type);
8532     num_bytes += putFile16BitBE(file, string_length);
8533
8534     for (i = 0; i < string_length; i++)
8535       num_bytes += putFile8Bit(file, string[i]);
8536   }
8537   else if (data_type == TYPE_ELEMENT_LIST)
8538   {
8539     int *element_array = (int *)(entry->value);
8540     int num_elements = *(int *)(entry->num_entities);
8541     int i;
8542
8543     // check if any settings have been modified before saving them
8544     for (i = 0; i < num_elements; i++)
8545       if (element_array[i] != default_value)
8546         modified = TRUE;
8547
8548     // do not save if explicitly told or if unmodified default settings
8549     if ((save_type == SAVE_CONF_NEVER) ||
8550         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8551       return 0;
8552
8553     if (write_element)
8554       num_bytes += putFile16BitBE(file, element);
8555
8556     num_bytes += putFile8Bit(file, conf_type);
8557     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8558
8559     for (i = 0; i < num_elements; i++)
8560       num_bytes += putFile16BitBE(file, element_array[i]);
8561   }
8562   else if (data_type == TYPE_CONTENT_LIST)
8563   {
8564     struct Content *content = (struct Content *)(entry->value);
8565     int num_contents = *(int *)(entry->num_entities);
8566     int i, x, y;
8567
8568     // check if any settings have been modified before saving them
8569     for (i = 0; i < num_contents; i++)
8570       for (y = 0; y < 3; y++)
8571         for (x = 0; x < 3; x++)
8572           if (content[i].e[x][y] != default_value)
8573             modified = TRUE;
8574
8575     // do not save if explicitly told or if unmodified default settings
8576     if ((save_type == SAVE_CONF_NEVER) ||
8577         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8578       return 0;
8579
8580     if (write_element)
8581       num_bytes += putFile16BitBE(file, element);
8582
8583     num_bytes += putFile8Bit(file, conf_type);
8584     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8585
8586     for (i = 0; i < num_contents; i++)
8587       for (y = 0; y < 3; y++)
8588         for (x = 0; x < 3; x++)
8589           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8590   }
8591
8592   return num_bytes;
8593 }
8594
8595 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8596 {
8597   int chunk_size = 0;
8598   int i;
8599
8600   li = *level;          // copy level data into temporary buffer
8601
8602   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8603     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8604
8605   return chunk_size;
8606 }
8607
8608 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8609 {
8610   int chunk_size = 0;
8611   int i;
8612
8613   li = *level;          // copy level data into temporary buffer
8614
8615   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8616     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8617
8618   return chunk_size;
8619 }
8620
8621 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8622 {
8623   int envelope_nr = element - EL_ENVELOPE_1;
8624   int chunk_size = 0;
8625   int i;
8626
8627   chunk_size += putFile16BitBE(file, element);
8628
8629   // copy envelope data into temporary buffer
8630   xx_envelope = level->envelope[envelope_nr];
8631
8632   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8633     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8634
8635   return chunk_size;
8636 }
8637
8638 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8639 {
8640   struct ElementInfo *ei = &element_info[element];
8641   int chunk_size = 0;
8642   int i, j;
8643
8644   chunk_size += putFile16BitBE(file, element);
8645
8646   xx_ei = *ei;          // copy element data into temporary buffer
8647
8648   // set default description string for this specific element
8649   strcpy(xx_default_description, getDefaultElementDescription(ei));
8650
8651   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8652     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8653
8654   for (i = 0; i < ei->num_change_pages; i++)
8655   {
8656     struct ElementChangeInfo *change = &ei->change_page[i];
8657
8658     xx_current_change_page = i;
8659
8660     xx_change = *change;        // copy change data into temporary buffer
8661
8662     resetEventBits();
8663     setEventBitsFromEventFlags(change);
8664
8665     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8666       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8667                                          FALSE);
8668   }
8669
8670   return chunk_size;
8671 }
8672
8673 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8674 {
8675   struct ElementInfo *ei = &element_info[element];
8676   struct ElementGroupInfo *group = ei->group;
8677   int chunk_size = 0;
8678   int i;
8679
8680   chunk_size += putFile16BitBE(file, element);
8681
8682   xx_ei = *ei;          // copy element data into temporary buffer
8683   xx_group = *group;    // copy group data into temporary buffer
8684
8685   // set default description string for this specific element
8686   strcpy(xx_default_description, getDefaultElementDescription(ei));
8687
8688   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8689     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8690
8691   return chunk_size;
8692 }
8693
8694 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8695 {
8696   struct ElementInfo *ei = &element_info[element];
8697   int chunk_size = 0;
8698   int i;
8699
8700   chunk_size += putFile16BitBE(file, element);
8701
8702   xx_ei = *ei;          // copy element data into temporary buffer
8703
8704   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8705     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8706
8707   return chunk_size;
8708 }
8709
8710 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8711                                   boolean save_as_template)
8712 {
8713   int chunk_size;
8714   int i;
8715   FILE *file;
8716
8717   if (!(file = fopen(filename, MODE_WRITE)))
8718   {
8719     Warn("cannot save level file '%s'", filename);
8720
8721     return;
8722   }
8723
8724   level->file_version = FILE_VERSION_ACTUAL;
8725   level->game_version = GAME_VERSION_ACTUAL;
8726
8727   level->creation_date = getCurrentDate();
8728
8729   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8730   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8731
8732   chunk_size = SaveLevel_VERS(NULL, level);
8733   putFileChunkBE(file, "VERS", chunk_size);
8734   SaveLevel_VERS(file, level);
8735
8736   chunk_size = SaveLevel_DATE(NULL, level);
8737   putFileChunkBE(file, "DATE", chunk_size);
8738   SaveLevel_DATE(file, level);
8739
8740   chunk_size = SaveLevel_NAME(NULL, level);
8741   putFileChunkBE(file, "NAME", chunk_size);
8742   SaveLevel_NAME(file, level);
8743
8744   chunk_size = SaveLevel_AUTH(NULL, level);
8745   putFileChunkBE(file, "AUTH", chunk_size);
8746   SaveLevel_AUTH(file, level);
8747
8748   chunk_size = SaveLevel_INFO(NULL, level);
8749   putFileChunkBE(file, "INFO", chunk_size);
8750   SaveLevel_INFO(file, level);
8751
8752   chunk_size = SaveLevel_BODY(NULL, level);
8753   putFileChunkBE(file, "BODY", chunk_size);
8754   SaveLevel_BODY(file, level);
8755
8756   chunk_size = SaveLevel_ELEM(NULL, level);
8757   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8758   {
8759     putFileChunkBE(file, "ELEM", chunk_size);
8760     SaveLevel_ELEM(file, level);
8761   }
8762
8763   for (i = 0; i < NUM_ENVELOPES; i++)
8764   {
8765     int element = EL_ENVELOPE_1 + i;
8766
8767     chunk_size = SaveLevel_NOTE(NULL, level, element);
8768     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8769     {
8770       putFileChunkBE(file, "NOTE", chunk_size);
8771       SaveLevel_NOTE(file, level, element);
8772     }
8773   }
8774
8775   // if not using template level, check for non-default custom/group elements
8776   if (!level->use_custom_template || save_as_template)
8777   {
8778     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8779     {
8780       int element = EL_CUSTOM_START + i;
8781
8782       chunk_size = SaveLevel_CUSX(NULL, level, element);
8783       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8784       {
8785         putFileChunkBE(file, "CUSX", chunk_size);
8786         SaveLevel_CUSX(file, level, element);
8787       }
8788     }
8789
8790     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8791     {
8792       int element = EL_GROUP_START + i;
8793
8794       chunk_size = SaveLevel_GRPX(NULL, level, element);
8795       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8796       {
8797         putFileChunkBE(file, "GRPX", chunk_size);
8798         SaveLevel_GRPX(file, level, element);
8799       }
8800     }
8801
8802     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8803     {
8804       int element = GET_EMPTY_ELEMENT(i);
8805
8806       chunk_size = SaveLevel_EMPX(NULL, level, element);
8807       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8808       {
8809         putFileChunkBE(file, "EMPX", chunk_size);
8810         SaveLevel_EMPX(file, level, element);
8811       }
8812     }
8813   }
8814
8815   fclose(file);
8816
8817   SetFilePermissions(filename, PERMS_PRIVATE);
8818 }
8819
8820 void SaveLevel(int nr)
8821 {
8822   char *filename = getDefaultLevelFilename(nr);
8823
8824   SaveLevelFromFilename(&level, filename, FALSE);
8825 }
8826
8827 void SaveLevelTemplate(void)
8828 {
8829   char *filename = getLocalLevelTemplateFilename();
8830
8831   SaveLevelFromFilename(&level, filename, TRUE);
8832 }
8833
8834 boolean SaveLevelChecked(int nr)
8835 {
8836   char *filename = getDefaultLevelFilename(nr);
8837   boolean new_level = !fileExists(filename);
8838   boolean level_saved = FALSE;
8839
8840   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8841   {
8842     SaveLevel(nr);
8843
8844     if (new_level)
8845       Request("Level saved!", REQ_CONFIRM);
8846
8847     level_saved = TRUE;
8848   }
8849
8850   return level_saved;
8851 }
8852
8853 void DumpLevel(struct LevelInfo *level)
8854 {
8855   if (level->no_level_file || level->no_valid_file)
8856   {
8857     Warn("cannot dump -- no valid level file found");
8858
8859     return;
8860   }
8861
8862   PrintLine("-", 79);
8863   Print("Level xxx (file version %08d, game version %08d)\n",
8864         level->file_version, level->game_version);
8865   PrintLine("-", 79);
8866
8867   Print("Level author: '%s'\n", level->author);
8868   Print("Level title:  '%s'\n", level->name);
8869   Print("\n");
8870   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8871   Print("\n");
8872   Print("Level time:  %d seconds\n", level->time);
8873   Print("Gems needed: %d\n", level->gems_needed);
8874   Print("\n");
8875   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8876   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8877   Print("Time for light:      %d seconds\n", level->time_light);
8878   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8879   Print("\n");
8880   Print("Amoeba speed: %d\n", level->amoeba_speed);
8881   Print("\n");
8882
8883   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8884   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8885   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8886   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8887   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8888   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8889
8890   if (options.debug)
8891   {
8892     int i, j;
8893
8894     for (i = 0; i < NUM_ENVELOPES; i++)
8895     {
8896       char *text = level->envelope[i].text;
8897       int text_len = strlen(text);
8898       boolean has_text = FALSE;
8899
8900       for (j = 0; j < text_len; j++)
8901         if (text[j] != ' ' && text[j] != '\n')
8902           has_text = TRUE;
8903
8904       if (has_text)
8905       {
8906         Print("\n");
8907         Print("Envelope %d:\n'%s'\n", i + 1, text);
8908       }
8909     }
8910   }
8911
8912   PrintLine("-", 79);
8913 }
8914
8915 void DumpLevels(void)
8916 {
8917   static LevelDirTree *dumplevel_leveldir = NULL;
8918
8919   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8920                                                  global.dumplevel_leveldir);
8921
8922   if (dumplevel_leveldir == NULL)
8923     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8924
8925   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8926       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8927     Fail("no such level number: %d", global.dumplevel_level_nr);
8928
8929   leveldir_current = dumplevel_leveldir;
8930
8931   LoadLevel(global.dumplevel_level_nr);
8932   DumpLevel(&level);
8933
8934   CloseAllAndExit(0);
8935 }
8936
8937
8938 // ============================================================================
8939 // tape file functions
8940 // ============================================================================
8941
8942 static void setTapeInfoToDefaults(void)
8943 {
8944   int i;
8945
8946   // always start with reliable default values (empty tape)
8947   TapeErase();
8948
8949   // default values (also for pre-1.2 tapes) with only the first player
8950   tape.player_participates[0] = TRUE;
8951   for (i = 1; i < MAX_PLAYERS; i++)
8952     tape.player_participates[i] = FALSE;
8953
8954   // at least one (default: the first) player participates in every tape
8955   tape.num_participating_players = 1;
8956
8957   tape.property_bits = TAPE_PROPERTY_NONE;
8958
8959   tape.level_nr = level_nr;
8960   tape.counter = 0;
8961   tape.changed = FALSE;
8962   tape.solved = FALSE;
8963
8964   tape.recording = FALSE;
8965   tape.playing = FALSE;
8966   tape.pausing = FALSE;
8967
8968   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8969   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8970
8971   tape.no_info_chunk = TRUE;
8972   tape.no_valid_file = FALSE;
8973 }
8974
8975 static int getTapePosSize(struct TapeInfo *tape)
8976 {
8977   int tape_pos_size = 0;
8978
8979   if (tape->use_key_actions)
8980     tape_pos_size += tape->num_participating_players;
8981
8982   if (tape->use_mouse_actions)
8983     tape_pos_size += 3;         // x and y position and mouse button mask
8984
8985   tape_pos_size += 1;           // tape action delay value
8986
8987   return tape_pos_size;
8988 }
8989
8990 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8991 {
8992   tape->use_key_actions = FALSE;
8993   tape->use_mouse_actions = FALSE;
8994
8995   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8996     tape->use_key_actions = TRUE;
8997
8998   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8999     tape->use_mouse_actions = TRUE;
9000 }
9001
9002 static int getTapeActionValue(struct TapeInfo *tape)
9003 {
9004   return (tape->use_key_actions &&
9005           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9006           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9007           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9008           TAPE_ACTIONS_DEFAULT);
9009 }
9010
9011 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9012 {
9013   tape->file_version = getFileVersion(file);
9014   tape->game_version = getFileVersion(file);
9015
9016   return chunk_size;
9017 }
9018
9019 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9020 {
9021   int i;
9022
9023   tape->random_seed = getFile32BitBE(file);
9024   tape->date        = getFile32BitBE(file);
9025   tape->length      = getFile32BitBE(file);
9026
9027   // read header fields that are new since version 1.2
9028   if (tape->file_version >= FILE_VERSION_1_2)
9029   {
9030     byte store_participating_players = getFile8Bit(file);
9031     int engine_version;
9032
9033     // since version 1.2, tapes store which players participate in the tape
9034     tape->num_participating_players = 0;
9035     for (i = 0; i < MAX_PLAYERS; i++)
9036     {
9037       tape->player_participates[i] = FALSE;
9038
9039       if (store_participating_players & (1 << i))
9040       {
9041         tape->player_participates[i] = TRUE;
9042         tape->num_participating_players++;
9043       }
9044     }
9045
9046     setTapeActionFlags(tape, getFile8Bit(file));
9047
9048     tape->property_bits = getFile8Bit(file);
9049     tape->solved = getFile8Bit(file);
9050
9051     engine_version = getFileVersion(file);
9052     if (engine_version > 0)
9053       tape->engine_version = engine_version;
9054     else
9055       tape->engine_version = tape->game_version;
9056   }
9057
9058   return chunk_size;
9059 }
9060
9061 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9062 {
9063   tape->scr_fieldx = getFile8Bit(file);
9064   tape->scr_fieldy = getFile8Bit(file);
9065
9066   return chunk_size;
9067 }
9068
9069 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9070 {
9071   char *level_identifier = NULL;
9072   int level_identifier_size;
9073   int i;
9074
9075   tape->no_info_chunk = FALSE;
9076
9077   level_identifier_size = getFile16BitBE(file);
9078
9079   level_identifier = checked_malloc(level_identifier_size);
9080
9081   for (i = 0; i < level_identifier_size; i++)
9082     level_identifier[i] = getFile8Bit(file);
9083
9084   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9085   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9086
9087   checked_free(level_identifier);
9088
9089   tape->level_nr = getFile16BitBE(file);
9090
9091   chunk_size = 2 + level_identifier_size + 2;
9092
9093   return chunk_size;
9094 }
9095
9096 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9097 {
9098   int i, j;
9099   int tape_pos_size = getTapePosSize(tape);
9100   int chunk_size_expected = tape_pos_size * tape->length;
9101
9102   if (chunk_size_expected != chunk_size)
9103   {
9104     ReadUnusedBytesFromFile(file, chunk_size);
9105     return chunk_size_expected;
9106   }
9107
9108   for (i = 0; i < tape->length; i++)
9109   {
9110     if (i >= MAX_TAPE_LEN)
9111     {
9112       Warn("tape truncated -- size exceeds maximum tape size %d",
9113             MAX_TAPE_LEN);
9114
9115       // tape too large; read and ignore remaining tape data from this chunk
9116       for (;i < tape->length; i++)
9117         ReadUnusedBytesFromFile(file, tape_pos_size);
9118
9119       break;
9120     }
9121
9122     if (tape->use_key_actions)
9123     {
9124       for (j = 0; j < MAX_PLAYERS; j++)
9125       {
9126         tape->pos[i].action[j] = MV_NONE;
9127
9128         if (tape->player_participates[j])
9129           tape->pos[i].action[j] = getFile8Bit(file);
9130       }
9131     }
9132
9133     if (tape->use_mouse_actions)
9134     {
9135       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9136       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9137       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9138     }
9139
9140     tape->pos[i].delay = getFile8Bit(file);
9141
9142     if (tape->file_version == FILE_VERSION_1_0)
9143     {
9144       // eliminate possible diagonal moves in old tapes
9145       // this is only for backward compatibility
9146
9147       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9148       byte action = tape->pos[i].action[0];
9149       int k, num_moves = 0;
9150
9151       for (k = 0; k < 4; k++)
9152       {
9153         if (action & joy_dir[k])
9154         {
9155           tape->pos[i + num_moves].action[0] = joy_dir[k];
9156           if (num_moves > 0)
9157             tape->pos[i + num_moves].delay = 0;
9158           num_moves++;
9159         }
9160       }
9161
9162       if (num_moves > 1)
9163       {
9164         num_moves--;
9165         i += num_moves;
9166         tape->length += num_moves;
9167       }
9168     }
9169     else if (tape->file_version < FILE_VERSION_2_0)
9170     {
9171       // convert pre-2.0 tapes to new tape format
9172
9173       if (tape->pos[i].delay > 1)
9174       {
9175         // action part
9176         tape->pos[i + 1] = tape->pos[i];
9177         tape->pos[i + 1].delay = 1;
9178
9179         // delay part
9180         for (j = 0; j < MAX_PLAYERS; j++)
9181           tape->pos[i].action[j] = MV_NONE;
9182         tape->pos[i].delay--;
9183
9184         i++;
9185         tape->length++;
9186       }
9187     }
9188
9189     if (checkEndOfFile(file))
9190       break;
9191   }
9192
9193   if (i != tape->length)
9194     chunk_size = tape_pos_size * i;
9195
9196   return chunk_size;
9197 }
9198
9199 static void LoadTape_SokobanSolution(char *filename)
9200 {
9201   File *file;
9202   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9203
9204   if (!(file = openFile(filename, MODE_READ)))
9205   {
9206     tape.no_valid_file = TRUE;
9207
9208     return;
9209   }
9210
9211   while (!checkEndOfFile(file))
9212   {
9213     unsigned char c = getByteFromFile(file);
9214
9215     if (checkEndOfFile(file))
9216       break;
9217
9218     switch (c)
9219     {
9220       case 'u':
9221       case 'U':
9222         tape.pos[tape.length].action[0] = MV_UP;
9223         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9224         tape.length++;
9225         break;
9226
9227       case 'd':
9228       case 'D':
9229         tape.pos[tape.length].action[0] = MV_DOWN;
9230         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9231         tape.length++;
9232         break;
9233
9234       case 'l':
9235       case 'L':
9236         tape.pos[tape.length].action[0] = MV_LEFT;
9237         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9238         tape.length++;
9239         break;
9240
9241       case 'r':
9242       case 'R':
9243         tape.pos[tape.length].action[0] = MV_RIGHT;
9244         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9245         tape.length++;
9246         break;
9247
9248       case '\n':
9249       case '\r':
9250       case '\t':
9251       case ' ':
9252         // ignore white-space characters
9253         break;
9254
9255       default:
9256         tape.no_valid_file = TRUE;
9257
9258         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9259
9260         break;
9261     }
9262   }
9263
9264   closeFile(file);
9265
9266   if (tape.no_valid_file)
9267     return;
9268
9269   tape.length_frames  = GetTapeLengthFrames();
9270   tape.length_seconds = GetTapeLengthSeconds();
9271 }
9272
9273 void LoadTapeFromFilename(char *filename)
9274 {
9275   char cookie[MAX_LINE_LEN];
9276   char chunk_name[CHUNK_ID_LEN + 1];
9277   File *file;
9278   int chunk_size;
9279
9280   // always start with reliable default values
9281   setTapeInfoToDefaults();
9282
9283   if (strSuffix(filename, ".sln"))
9284   {
9285     LoadTape_SokobanSolution(filename);
9286
9287     return;
9288   }
9289
9290   if (!(file = openFile(filename, MODE_READ)))
9291   {
9292     tape.no_valid_file = TRUE;
9293
9294     return;
9295   }
9296
9297   getFileChunkBE(file, chunk_name, NULL);
9298   if (strEqual(chunk_name, "RND1"))
9299   {
9300     getFile32BitBE(file);               // not used
9301
9302     getFileChunkBE(file, chunk_name, NULL);
9303     if (!strEqual(chunk_name, "TAPE"))
9304     {
9305       tape.no_valid_file = TRUE;
9306
9307       Warn("unknown format of tape file '%s'", filename);
9308
9309       closeFile(file);
9310
9311       return;
9312     }
9313   }
9314   else  // check for pre-2.0 file format with cookie string
9315   {
9316     strcpy(cookie, chunk_name);
9317     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9318       cookie[4] = '\0';
9319     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9320       cookie[strlen(cookie) - 1] = '\0';
9321
9322     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9323     {
9324       tape.no_valid_file = TRUE;
9325
9326       Warn("unknown format of tape file '%s'", filename);
9327
9328       closeFile(file);
9329
9330       return;
9331     }
9332
9333     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9334     {
9335       tape.no_valid_file = TRUE;
9336
9337       Warn("unsupported version of tape file '%s'", filename);
9338
9339       closeFile(file);
9340
9341       return;
9342     }
9343
9344     // pre-2.0 tape files have no game version, so use file version here
9345     tape.game_version = tape.file_version;
9346   }
9347
9348   if (tape.file_version < FILE_VERSION_1_2)
9349   {
9350     // tape files from versions before 1.2.0 without chunk structure
9351     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9352     LoadTape_BODY(file, 2 * tape.length,      &tape);
9353   }
9354   else
9355   {
9356     static struct
9357     {
9358       char *name;
9359       int size;
9360       int (*loader)(File *, int, struct TapeInfo *);
9361     }
9362     chunk_info[] =
9363     {
9364       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9365       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9366       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9367       { "INFO", -1,                     LoadTape_INFO },
9368       { "BODY", -1,                     LoadTape_BODY },
9369       {  NULL,  0,                      NULL }
9370     };
9371
9372     while (getFileChunkBE(file, chunk_name, &chunk_size))
9373     {
9374       int i = 0;
9375
9376       while (chunk_info[i].name != NULL &&
9377              !strEqual(chunk_name, chunk_info[i].name))
9378         i++;
9379
9380       if (chunk_info[i].name == NULL)
9381       {
9382         Warn("unknown chunk '%s' in tape file '%s'",
9383               chunk_name, filename);
9384
9385         ReadUnusedBytesFromFile(file, chunk_size);
9386       }
9387       else if (chunk_info[i].size != -1 &&
9388                chunk_info[i].size != chunk_size)
9389       {
9390         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9391               chunk_size, chunk_name, filename);
9392
9393         ReadUnusedBytesFromFile(file, chunk_size);
9394       }
9395       else
9396       {
9397         // call function to load this tape chunk
9398         int chunk_size_expected =
9399           (chunk_info[i].loader)(file, chunk_size, &tape);
9400
9401         // the size of some chunks cannot be checked before reading other
9402         // chunks first (like "HEAD" and "BODY") that contain some header
9403         // information, so check them here
9404         if (chunk_size_expected != chunk_size)
9405         {
9406           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9407                 chunk_size, chunk_name, filename);
9408         }
9409       }
9410     }
9411   }
9412
9413   closeFile(file);
9414
9415   tape.length_frames  = GetTapeLengthFrames();
9416   tape.length_seconds = GetTapeLengthSeconds();
9417
9418 #if 0
9419   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9420         tape.file_version);
9421   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9422         tape.game_version);
9423   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9424         tape.engine_version);
9425 #endif
9426 }
9427
9428 void LoadTape(int nr)
9429 {
9430   char *filename = getTapeFilename(nr);
9431
9432   LoadTapeFromFilename(filename);
9433 }
9434
9435 void LoadSolutionTape(int nr)
9436 {
9437   char *filename = getSolutionTapeFilename(nr);
9438
9439   LoadTapeFromFilename(filename);
9440
9441   if (TAPE_IS_EMPTY(tape))
9442   {
9443     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9444         level.native_bd_level->replay != NULL)
9445       CopyNativeTape_BD_to_RND(&level);
9446     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9447         level.native_sp_level->demo.is_available)
9448       CopyNativeTape_SP_to_RND(&level);
9449   }
9450 }
9451
9452 void LoadScoreTape(char *score_tape_basename, int nr)
9453 {
9454   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9455
9456   LoadTapeFromFilename(filename);
9457 }
9458
9459 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9460 {
9461   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9462
9463   LoadTapeFromFilename(filename);
9464 }
9465
9466 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9467 {
9468   // chunk required for team mode tapes with non-default screen size
9469   return (tape->num_participating_players > 1 &&
9470           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9471            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9472 }
9473
9474 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9475 {
9476   putFileVersion(file, tape->file_version);
9477   putFileVersion(file, tape->game_version);
9478 }
9479
9480 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9481 {
9482   int i;
9483   byte store_participating_players = 0;
9484
9485   // set bits for participating players for compact storage
9486   for (i = 0; i < MAX_PLAYERS; i++)
9487     if (tape->player_participates[i])
9488       store_participating_players |= (1 << i);
9489
9490   putFile32BitBE(file, tape->random_seed);
9491   putFile32BitBE(file, tape->date);
9492   putFile32BitBE(file, tape->length);
9493
9494   putFile8Bit(file, store_participating_players);
9495
9496   putFile8Bit(file, getTapeActionValue(tape));
9497
9498   putFile8Bit(file, tape->property_bits);
9499   putFile8Bit(file, tape->solved);
9500
9501   putFileVersion(file, tape->engine_version);
9502 }
9503
9504 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9505 {
9506   putFile8Bit(file, tape->scr_fieldx);
9507   putFile8Bit(file, tape->scr_fieldy);
9508 }
9509
9510 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9511 {
9512   int level_identifier_size = strlen(tape->level_identifier) + 1;
9513   int i;
9514
9515   putFile16BitBE(file, level_identifier_size);
9516
9517   for (i = 0; i < level_identifier_size; i++)
9518     putFile8Bit(file, tape->level_identifier[i]);
9519
9520   putFile16BitBE(file, tape->level_nr);
9521 }
9522
9523 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9524 {
9525   int i, j;
9526
9527   for (i = 0; i < tape->length; i++)
9528   {
9529     if (tape->use_key_actions)
9530     {
9531       for (j = 0; j < MAX_PLAYERS; j++)
9532         if (tape->player_participates[j])
9533           putFile8Bit(file, tape->pos[i].action[j]);
9534     }
9535
9536     if (tape->use_mouse_actions)
9537     {
9538       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9539       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9540       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9541     }
9542
9543     putFile8Bit(file, tape->pos[i].delay);
9544   }
9545 }
9546
9547 void SaveTapeToFilename(char *filename)
9548 {
9549   FILE *file;
9550   int tape_pos_size;
9551   int info_chunk_size;
9552   int body_chunk_size;
9553
9554   if (!(file = fopen(filename, MODE_WRITE)))
9555   {
9556     Warn("cannot save level recording file '%s'", filename);
9557
9558     return;
9559   }
9560
9561   tape_pos_size = getTapePosSize(&tape);
9562
9563   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9564   body_chunk_size = tape_pos_size * tape.length;
9565
9566   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9567   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9568
9569   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9570   SaveTape_VERS(file, &tape);
9571
9572   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9573   SaveTape_HEAD(file, &tape);
9574
9575   if (checkSaveTape_SCRN(&tape))
9576   {
9577     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9578     SaveTape_SCRN(file, &tape);
9579   }
9580
9581   putFileChunkBE(file, "INFO", info_chunk_size);
9582   SaveTape_INFO(file, &tape);
9583
9584   putFileChunkBE(file, "BODY", body_chunk_size);
9585   SaveTape_BODY(file, &tape);
9586
9587   fclose(file);
9588
9589   SetFilePermissions(filename, PERMS_PRIVATE);
9590 }
9591
9592 static void SaveTapeExt(char *filename)
9593 {
9594   int i;
9595
9596   tape.file_version = FILE_VERSION_ACTUAL;
9597   tape.game_version = GAME_VERSION_ACTUAL;
9598
9599   tape.num_participating_players = 0;
9600
9601   // count number of participating players
9602   for (i = 0; i < MAX_PLAYERS; i++)
9603     if (tape.player_participates[i])
9604       tape.num_participating_players++;
9605
9606   SaveTapeToFilename(filename);
9607
9608   tape.changed = FALSE;
9609 }
9610
9611 void SaveTape(int nr)
9612 {
9613   char *filename = getTapeFilename(nr);
9614
9615   InitTapeDirectory(leveldir_current->subdir);
9616
9617   SaveTapeExt(filename);
9618 }
9619
9620 void SaveScoreTape(int nr)
9621 {
9622   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9623
9624   // used instead of "leveldir_current->subdir" (for network games)
9625   InitScoreTapeDirectory(levelset.identifier, nr);
9626
9627   SaveTapeExt(filename);
9628 }
9629
9630 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9631                                   unsigned int req_state_added)
9632 {
9633   char *filename = getTapeFilename(nr);
9634   boolean new_tape = !fileExists(filename);
9635   boolean tape_saved = FALSE;
9636
9637   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9638   {
9639     SaveTape(nr);
9640
9641     if (new_tape)
9642       Request(msg_saved, REQ_CONFIRM | req_state_added);
9643
9644     tape_saved = TRUE;
9645   }
9646
9647   return tape_saved;
9648 }
9649
9650 boolean SaveTapeChecked(int nr)
9651 {
9652   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9653 }
9654
9655 boolean SaveTapeChecked_LevelSolved(int nr)
9656 {
9657   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9658                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9659 }
9660
9661 void DumpTape(struct TapeInfo *tape)
9662 {
9663   int tape_frame_counter;
9664   int i, j;
9665
9666   if (tape->no_valid_file)
9667   {
9668     Warn("cannot dump -- no valid tape file found");
9669
9670     return;
9671   }
9672
9673   PrintLine("-", 79);
9674
9675   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9676         tape->level_nr, tape->file_version, tape->game_version);
9677   Print("                  (effective engine version %08d)\n",
9678         tape->engine_version);
9679   Print("Level series identifier: '%s'\n", tape->level_identifier);
9680
9681   Print("Solution tape: %s\n",
9682         tape->solved ? "yes" :
9683         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9684
9685   Print("Special tape properties: ");
9686   if (tape->property_bits == TAPE_PROPERTY_NONE)
9687     Print("[none]");
9688   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9689     Print("[em_random_bug]");
9690   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9691     Print("[game_speed]");
9692   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9693     Print("[pause]");
9694   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9695     Print("[single_step]");
9696   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9697     Print("[snapshot]");
9698   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9699     Print("[replayed]");
9700   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9701     Print("[tas_keys]");
9702   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9703     Print("[small_graphics]");
9704   Print("\n");
9705
9706   int year2 = tape->date / 10000;
9707   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9708   int month_index_raw = (tape->date / 100) % 100;
9709   int month_index = month_index_raw % 12;       // prevent invalid index
9710   int month = month_index + 1;
9711   int day = tape->date % 100;
9712
9713   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9714
9715   PrintLine("-", 79);
9716
9717   tape_frame_counter = 0;
9718
9719   for (i = 0; i < tape->length; i++)
9720   {
9721     if (i >= MAX_TAPE_LEN)
9722       break;
9723
9724     Print("%04d: ", i);
9725
9726     for (j = 0; j < MAX_PLAYERS; j++)
9727     {
9728       if (tape->player_participates[j])
9729       {
9730         int action = tape->pos[i].action[j];
9731
9732         Print("%d:%02x ", j, action);
9733         Print("[%c%c%c%c|%c%c] - ",
9734               (action & JOY_LEFT ? '<' : ' '),
9735               (action & JOY_RIGHT ? '>' : ' '),
9736               (action & JOY_UP ? '^' : ' '),
9737               (action & JOY_DOWN ? 'v' : ' '),
9738               (action & JOY_BUTTON_1 ? '1' : ' '),
9739               (action & JOY_BUTTON_2 ? '2' : ' '));
9740       }
9741     }
9742
9743     Print("(%03d) ", tape->pos[i].delay);
9744     Print("[%05d]\n", tape_frame_counter);
9745
9746     tape_frame_counter += tape->pos[i].delay;
9747   }
9748
9749   PrintLine("-", 79);
9750 }
9751
9752 void DumpTapes(void)
9753 {
9754   static LevelDirTree *dumptape_leveldir = NULL;
9755
9756   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9757                                                 global.dumptape_leveldir);
9758
9759   if (dumptape_leveldir == NULL)
9760     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9761
9762   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9763       global.dumptape_level_nr > dumptape_leveldir->last_level)
9764     Fail("no such level number: %d", global.dumptape_level_nr);
9765
9766   leveldir_current = dumptape_leveldir;
9767
9768   if (options.mytapes)
9769     LoadTape(global.dumptape_level_nr);
9770   else
9771     LoadSolutionTape(global.dumptape_level_nr);
9772
9773   DumpTape(&tape);
9774
9775   CloseAllAndExit(0);
9776 }
9777
9778
9779 // ============================================================================
9780 // score file functions
9781 // ============================================================================
9782
9783 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9784 {
9785   int i;
9786
9787   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9788   {
9789     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9790     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9791     scores->entry[i].score = 0;
9792     scores->entry[i].time = 0;
9793
9794     scores->entry[i].id = -1;
9795     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9796     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9797     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9798     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9799     strcpy(scores->entry[i].country_code, "??");
9800   }
9801
9802   scores->num_entries = 0;
9803   scores->last_added = -1;
9804   scores->last_added_local = -1;
9805
9806   scores->updated = FALSE;
9807   scores->uploaded = FALSE;
9808   scores->tape_downloaded = FALSE;
9809   scores->force_last_added = FALSE;
9810
9811   // The following values are intentionally not reset here:
9812   // - last_level_nr
9813   // - last_entry_nr
9814   // - next_level_nr
9815   // - continue_playing
9816   // - continue_on_return
9817 }
9818
9819 static void setScoreInfoToDefaults(void)
9820 {
9821   setScoreInfoToDefaultsExt(&scores);
9822 }
9823
9824 static void setServerScoreInfoToDefaults(void)
9825 {
9826   setScoreInfoToDefaultsExt(&server_scores);
9827 }
9828
9829 static void LoadScore_OLD(int nr)
9830 {
9831   int i;
9832   char *filename = getScoreFilename(nr);
9833   char cookie[MAX_LINE_LEN];
9834   char line[MAX_LINE_LEN];
9835   char *line_ptr;
9836   FILE *file;
9837
9838   if (!(file = fopen(filename, MODE_READ)))
9839     return;
9840
9841   // check file identifier
9842   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9843     cookie[0] = '\0';
9844   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9845     cookie[strlen(cookie) - 1] = '\0';
9846
9847   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9848   {
9849     Warn("unknown format of score file '%s'", filename);
9850
9851     fclose(file);
9852
9853     return;
9854   }
9855
9856   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9857   {
9858     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9859       Warn("fscanf() failed; %s", strerror(errno));
9860
9861     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9862       line[0] = '\0';
9863
9864     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9865       line[strlen(line) - 1] = '\0';
9866
9867     for (line_ptr = line; *line_ptr; line_ptr++)
9868     {
9869       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9870       {
9871         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9872         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9873         break;
9874       }
9875     }
9876   }
9877
9878   fclose(file);
9879 }
9880
9881 static void ConvertScore_OLD(void)
9882 {
9883   // only convert score to time for levels that rate playing time over score
9884   if (!level.rate_time_over_score)
9885     return;
9886
9887   // convert old score to playing time for score-less levels (like Supaplex)
9888   int time_final_max = 999;
9889   int i;
9890
9891   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9892   {
9893     int score = scores.entry[i].score;
9894
9895     if (score > 0 && score < time_final_max)
9896       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9897   }
9898 }
9899
9900 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9901 {
9902   scores->file_version = getFileVersion(file);
9903   scores->game_version = getFileVersion(file);
9904
9905   return chunk_size;
9906 }
9907
9908 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9909 {
9910   char *level_identifier = NULL;
9911   int level_identifier_size;
9912   int i;
9913
9914   level_identifier_size = getFile16BitBE(file);
9915
9916   level_identifier = checked_malloc(level_identifier_size);
9917
9918   for (i = 0; i < level_identifier_size; i++)
9919     level_identifier[i] = getFile8Bit(file);
9920
9921   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9922   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9923
9924   checked_free(level_identifier);
9925
9926   scores->level_nr = getFile16BitBE(file);
9927   scores->num_entries = getFile16BitBE(file);
9928
9929   chunk_size = 2 + level_identifier_size + 2 + 2;
9930
9931   return chunk_size;
9932 }
9933
9934 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9935 {
9936   int i, j;
9937
9938   for (i = 0; i < scores->num_entries; i++)
9939   {
9940     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9941       scores->entry[i].name[j] = getFile8Bit(file);
9942
9943     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9944   }
9945
9946   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9947
9948   return chunk_size;
9949 }
9950
9951 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9952 {
9953   int i;
9954
9955   for (i = 0; i < scores->num_entries; i++)
9956     scores->entry[i].score = getFile16BitBE(file);
9957
9958   chunk_size = scores->num_entries * 2;
9959
9960   return chunk_size;
9961 }
9962
9963 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9964 {
9965   int i;
9966
9967   for (i = 0; i < scores->num_entries; i++)
9968     scores->entry[i].score = getFile32BitBE(file);
9969
9970   chunk_size = scores->num_entries * 4;
9971
9972   return chunk_size;
9973 }
9974
9975 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9976 {
9977   int i;
9978
9979   for (i = 0; i < scores->num_entries; i++)
9980     scores->entry[i].time = getFile32BitBE(file);
9981
9982   chunk_size = scores->num_entries * 4;
9983
9984   return chunk_size;
9985 }
9986
9987 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9988 {
9989   int i, j;
9990
9991   for (i = 0; i < scores->num_entries; i++)
9992   {
9993     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9994       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9995
9996     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9997   }
9998
9999   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10000
10001   return chunk_size;
10002 }
10003
10004 void LoadScore(int nr)
10005 {
10006   char *filename = getScoreFilename(nr);
10007   char cookie[MAX_LINE_LEN];
10008   char chunk_name[CHUNK_ID_LEN + 1];
10009   int chunk_size;
10010   boolean old_score_file_format = FALSE;
10011   File *file;
10012
10013   // always start with reliable default values
10014   setScoreInfoToDefaults();
10015
10016   if (!(file = openFile(filename, MODE_READ)))
10017     return;
10018
10019   getFileChunkBE(file, chunk_name, NULL);
10020   if (strEqual(chunk_name, "RND1"))
10021   {
10022     getFile32BitBE(file);               // not used
10023
10024     getFileChunkBE(file, chunk_name, NULL);
10025     if (!strEqual(chunk_name, "SCOR"))
10026     {
10027       Warn("unknown format of score file '%s'", filename);
10028
10029       closeFile(file);
10030
10031       return;
10032     }
10033   }
10034   else  // check for old file format with cookie string
10035   {
10036     strcpy(cookie, chunk_name);
10037     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10038       cookie[4] = '\0';
10039     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10040       cookie[strlen(cookie) - 1] = '\0';
10041
10042     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10043     {
10044       Warn("unknown format of score file '%s'", filename);
10045
10046       closeFile(file);
10047
10048       return;
10049     }
10050
10051     old_score_file_format = TRUE;
10052   }
10053
10054   if (old_score_file_format)
10055   {
10056     // score files from versions before 4.2.4.0 without chunk structure
10057     LoadScore_OLD(nr);
10058
10059     // convert score to time, if possible (mainly for Supaplex levels)
10060     ConvertScore_OLD();
10061   }
10062   else
10063   {
10064     static struct
10065     {
10066       char *name;
10067       int size;
10068       int (*loader)(File *, int, struct ScoreInfo *);
10069     }
10070     chunk_info[] =
10071     {
10072       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10073       { "INFO", -1,                     LoadScore_INFO },
10074       { "NAME", -1,                     LoadScore_NAME },
10075       { "SCOR", -1,                     LoadScore_SCOR },
10076       { "SC4R", -1,                     LoadScore_SC4R },
10077       { "TIME", -1,                     LoadScore_TIME },
10078       { "TAPE", -1,                     LoadScore_TAPE },
10079
10080       {  NULL,  0,                      NULL }
10081     };
10082
10083     while (getFileChunkBE(file, chunk_name, &chunk_size))
10084     {
10085       int i = 0;
10086
10087       while (chunk_info[i].name != NULL &&
10088              !strEqual(chunk_name, chunk_info[i].name))
10089         i++;
10090
10091       if (chunk_info[i].name == NULL)
10092       {
10093         Warn("unknown chunk '%s' in score file '%s'",
10094               chunk_name, filename);
10095
10096         ReadUnusedBytesFromFile(file, chunk_size);
10097       }
10098       else if (chunk_info[i].size != -1 &&
10099                chunk_info[i].size != chunk_size)
10100       {
10101         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10102               chunk_size, chunk_name, filename);
10103
10104         ReadUnusedBytesFromFile(file, chunk_size);
10105       }
10106       else
10107       {
10108         // call function to load this score chunk
10109         int chunk_size_expected =
10110           (chunk_info[i].loader)(file, chunk_size, &scores);
10111
10112         // the size of some chunks cannot be checked before reading other
10113         // chunks first (like "HEAD" and "BODY") that contain some header
10114         // information, so check them here
10115         if (chunk_size_expected != chunk_size)
10116         {
10117           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10118                 chunk_size, chunk_name, filename);
10119         }
10120       }
10121     }
10122   }
10123
10124   closeFile(file);
10125 }
10126
10127 #if ENABLE_HISTORIC_CHUNKS
10128 void SaveScore_OLD(int nr)
10129 {
10130   int i;
10131   char *filename = getScoreFilename(nr);
10132   FILE *file;
10133
10134   // used instead of "leveldir_current->subdir" (for network games)
10135   InitScoreDirectory(levelset.identifier);
10136
10137   if (!(file = fopen(filename, MODE_WRITE)))
10138   {
10139     Warn("cannot save score for level %d", nr);
10140
10141     return;
10142   }
10143
10144   fprintf(file, "%s\n\n", SCORE_COOKIE);
10145
10146   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10147     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10148
10149   fclose(file);
10150
10151   SetFilePermissions(filename, PERMS_PRIVATE);
10152 }
10153 #endif
10154
10155 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10156 {
10157   putFileVersion(file, scores->file_version);
10158   putFileVersion(file, scores->game_version);
10159 }
10160
10161 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10162 {
10163   int level_identifier_size = strlen(scores->level_identifier) + 1;
10164   int i;
10165
10166   putFile16BitBE(file, level_identifier_size);
10167
10168   for (i = 0; i < level_identifier_size; i++)
10169     putFile8Bit(file, scores->level_identifier[i]);
10170
10171   putFile16BitBE(file, scores->level_nr);
10172   putFile16BitBE(file, scores->num_entries);
10173 }
10174
10175 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10176 {
10177   int i, j;
10178
10179   for (i = 0; i < scores->num_entries; i++)
10180   {
10181     int name_size = strlen(scores->entry[i].name);
10182
10183     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10184       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10185   }
10186 }
10187
10188 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10189 {
10190   int i;
10191
10192   for (i = 0; i < scores->num_entries; i++)
10193     putFile16BitBE(file, scores->entry[i].score);
10194 }
10195
10196 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10197 {
10198   int i;
10199
10200   for (i = 0; i < scores->num_entries; i++)
10201     putFile32BitBE(file, scores->entry[i].score);
10202 }
10203
10204 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10205 {
10206   int i;
10207
10208   for (i = 0; i < scores->num_entries; i++)
10209     putFile32BitBE(file, scores->entry[i].time);
10210 }
10211
10212 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10213 {
10214   int i, j;
10215
10216   for (i = 0; i < scores->num_entries; i++)
10217   {
10218     int size = strlen(scores->entry[i].tape_basename);
10219
10220     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10221       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10222   }
10223 }
10224
10225 static void SaveScoreToFilename(char *filename)
10226 {
10227   FILE *file;
10228   int info_chunk_size;
10229   int name_chunk_size;
10230   int scor_chunk_size;
10231   int sc4r_chunk_size;
10232   int time_chunk_size;
10233   int tape_chunk_size;
10234   boolean has_large_score_values;
10235   int i;
10236
10237   if (!(file = fopen(filename, MODE_WRITE)))
10238   {
10239     Warn("cannot save score file '%s'", filename);
10240
10241     return;
10242   }
10243
10244   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10245   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10246   scor_chunk_size = scores.num_entries * 2;
10247   sc4r_chunk_size = scores.num_entries * 4;
10248   time_chunk_size = scores.num_entries * 4;
10249   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10250
10251   has_large_score_values = FALSE;
10252   for (i = 0; i < scores.num_entries; i++)
10253     if (scores.entry[i].score > 0xffff)
10254       has_large_score_values = TRUE;
10255
10256   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10257   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10258
10259   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10260   SaveScore_VERS(file, &scores);
10261
10262   putFileChunkBE(file, "INFO", info_chunk_size);
10263   SaveScore_INFO(file, &scores);
10264
10265   putFileChunkBE(file, "NAME", name_chunk_size);
10266   SaveScore_NAME(file, &scores);
10267
10268   if (has_large_score_values)
10269   {
10270     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10271     SaveScore_SC4R(file, &scores);
10272   }
10273   else
10274   {
10275     putFileChunkBE(file, "SCOR", scor_chunk_size);
10276     SaveScore_SCOR(file, &scores);
10277   }
10278
10279   putFileChunkBE(file, "TIME", time_chunk_size);
10280   SaveScore_TIME(file, &scores);
10281
10282   putFileChunkBE(file, "TAPE", tape_chunk_size);
10283   SaveScore_TAPE(file, &scores);
10284
10285   fclose(file);
10286
10287   SetFilePermissions(filename, PERMS_PRIVATE);
10288 }
10289
10290 void SaveScore(int nr)
10291 {
10292   char *filename = getScoreFilename(nr);
10293   int i;
10294
10295   // used instead of "leveldir_current->subdir" (for network games)
10296   InitScoreDirectory(levelset.identifier);
10297
10298   scores.file_version = FILE_VERSION_ACTUAL;
10299   scores.game_version = GAME_VERSION_ACTUAL;
10300
10301   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10302   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10303   scores.level_nr = level_nr;
10304
10305   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10306     if (scores.entry[i].score == 0 &&
10307         scores.entry[i].time == 0 &&
10308         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10309       break;
10310
10311   scores.num_entries = i;
10312
10313   if (scores.num_entries == 0)
10314     return;
10315
10316   SaveScoreToFilename(filename);
10317 }
10318
10319 static void LoadServerScoreFromCache(int nr)
10320 {
10321   struct ScoreEntry score_entry;
10322   struct
10323   {
10324     void *value;
10325     boolean is_string;
10326     int string_size;
10327   }
10328   score_mapping[] =
10329   {
10330     { &score_entry.score,               FALSE,  0                       },
10331     { &score_entry.time,                FALSE,  0                       },
10332     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10333     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10334     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10335     { &score_entry.id,                  FALSE,  0                       },
10336     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10337     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10338     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10339     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10340
10341     { NULL,                             FALSE,  0                       }
10342   };
10343   char *filename = getScoreCacheFilename(nr);
10344   SetupFileHash *score_hash = loadSetupFileHash(filename);
10345   int i, j;
10346
10347   server_scores.num_entries = 0;
10348
10349   if (score_hash == NULL)
10350     return;
10351
10352   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10353   {
10354     score_entry = server_scores.entry[i];
10355
10356     for (j = 0; score_mapping[j].value != NULL; j++)
10357     {
10358       char token[10];
10359
10360       sprintf(token, "%02d.%d", i, j);
10361
10362       char *value = getHashEntry(score_hash, token);
10363
10364       if (value == NULL)
10365         continue;
10366
10367       if (score_mapping[j].is_string)
10368       {
10369         char *score_value = (char *)score_mapping[j].value;
10370         int value_size = score_mapping[j].string_size;
10371
10372         strncpy(score_value, value, value_size);
10373         score_value[value_size] = '\0';
10374       }
10375       else
10376       {
10377         int *score_value = (int *)score_mapping[j].value;
10378
10379         *score_value = atoi(value);
10380       }
10381
10382       server_scores.num_entries = i + 1;
10383     }
10384
10385     server_scores.entry[i] = score_entry;
10386   }
10387
10388   freeSetupFileHash(score_hash);
10389 }
10390
10391 void LoadServerScore(int nr, boolean download_score)
10392 {
10393   if (!setup.use_api_server)
10394     return;
10395
10396   // always start with reliable default values
10397   setServerScoreInfoToDefaults();
10398
10399   // 1st step: load server scores from cache file (which may not exist)
10400   // (this should prevent reading it while the thread is writing to it)
10401   LoadServerScoreFromCache(nr);
10402
10403   if (download_score && runtime.use_api_server)
10404   {
10405     // 2nd step: download server scores from score server to cache file
10406     // (as thread, as it might time out if the server is not reachable)
10407     ApiGetScoreAsThread(nr);
10408   }
10409 }
10410
10411 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10412 {
10413   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10414
10415   // if score tape not uploaded, ask for uploading missing tapes later
10416   if (!setup.has_remaining_tapes)
10417     setup.ask_for_remaining_tapes = TRUE;
10418
10419   setup.provide_uploading_tapes = TRUE;
10420   setup.has_remaining_tapes = TRUE;
10421
10422   SaveSetup_ServerSetup();
10423 }
10424
10425 void SaveServerScore(int nr, boolean tape_saved)
10426 {
10427   if (!runtime.use_api_server)
10428   {
10429     PrepareScoreTapesForUpload(leveldir_current->subdir);
10430
10431     return;
10432   }
10433
10434   ApiAddScoreAsThread(nr, tape_saved, NULL);
10435 }
10436
10437 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10438                              char *score_tape_filename)
10439 {
10440   if (!runtime.use_api_server)
10441     return;
10442
10443   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10444 }
10445
10446 void LoadLocalAndServerScore(int nr, boolean download_score)
10447 {
10448   int last_added_local = scores.last_added_local;
10449   boolean force_last_added = scores.force_last_added;
10450
10451   // needed if only showing server scores
10452   setScoreInfoToDefaults();
10453
10454   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10455     LoadScore(nr);
10456
10457   // restore last added local score entry (before merging server scores)
10458   scores.last_added = scores.last_added_local = last_added_local;
10459
10460   if (setup.use_api_server &&
10461       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10462   {
10463     // load server scores from cache file and trigger update from server
10464     LoadServerScore(nr, download_score);
10465
10466     // merge local scores with scores from server
10467     MergeServerScore();
10468   }
10469
10470   if (force_last_added)
10471     scores.force_last_added = force_last_added;
10472 }
10473
10474
10475 // ============================================================================
10476 // setup file functions
10477 // ============================================================================
10478
10479 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10480
10481
10482 static struct TokenInfo global_setup_tokens[] =
10483 {
10484   {
10485     TYPE_STRING,
10486     &setup.player_name,                         "player_name"
10487   },
10488   {
10489     TYPE_SWITCH,
10490     &setup.multiple_users,                      "multiple_users"
10491   },
10492   {
10493     TYPE_SWITCH,
10494     &setup.sound,                               "sound"
10495   },
10496   {
10497     TYPE_SWITCH,
10498     &setup.sound_loops,                         "repeating_sound_loops"
10499   },
10500   {
10501     TYPE_SWITCH,
10502     &setup.sound_music,                         "background_music"
10503   },
10504   {
10505     TYPE_SWITCH,
10506     &setup.sound_simple,                        "simple_sound_effects"
10507   },
10508   {
10509     TYPE_SWITCH,
10510     &setup.toons,                               "toons"
10511   },
10512   {
10513     TYPE_SWITCH,
10514     &setup.global_animations,                   "global_animations"
10515   },
10516   {
10517     TYPE_SWITCH,
10518     &setup.scroll_delay,                        "scroll_delay"
10519   },
10520   {
10521     TYPE_SWITCH,
10522     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10523   },
10524   {
10525     TYPE_INTEGER,
10526     &setup.scroll_delay_value,                  "scroll_delay_value"
10527   },
10528   {
10529     TYPE_STRING,
10530     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10531   },
10532   {
10533     TYPE_INTEGER,
10534     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10535   },
10536   {
10537     TYPE_SWITCH,
10538     &setup.fade_screens,                        "fade_screens"
10539   },
10540   {
10541     TYPE_SWITCH,
10542     &setup.autorecord,                          "automatic_tape_recording"
10543   },
10544   {
10545     TYPE_SWITCH,
10546     &setup.autorecord_after_replay,             "autorecord_after_replay"
10547   },
10548   {
10549     TYPE_SWITCH,
10550     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10551   },
10552   {
10553     TYPE_SWITCH,
10554     &setup.show_titlescreen,                    "show_titlescreen"
10555   },
10556   {
10557     TYPE_SWITCH,
10558     &setup.quick_doors,                         "quick_doors"
10559   },
10560   {
10561     TYPE_SWITCH,
10562     &setup.team_mode,                           "team_mode"
10563   },
10564   {
10565     TYPE_SWITCH,
10566     &setup.handicap,                            "handicap"
10567   },
10568   {
10569     TYPE_SWITCH,
10570     &setup.skip_levels,                         "skip_levels"
10571   },
10572   {
10573     TYPE_SWITCH,
10574     &setup.increment_levels,                    "increment_levels"
10575   },
10576   {
10577     TYPE_SWITCH,
10578     &setup.auto_play_next_level,                "auto_play_next_level"
10579   },
10580   {
10581     TYPE_SWITCH,
10582     &setup.count_score_after_game,              "count_score_after_game"
10583   },
10584   {
10585     TYPE_SWITCH,
10586     &setup.show_scores_after_game,              "show_scores_after_game"
10587   },
10588   {
10589     TYPE_SWITCH,
10590     &setup.time_limit,                          "time_limit"
10591   },
10592   {
10593     TYPE_SWITCH,
10594     &setup.fullscreen,                          "fullscreen"
10595   },
10596   {
10597     TYPE_INTEGER,
10598     &setup.window_scaling_percent,              "window_scaling_percent"
10599   },
10600   {
10601     TYPE_STRING,
10602     &setup.window_scaling_quality,              "window_scaling_quality"
10603   },
10604   {
10605     TYPE_STRING,
10606     &setup.screen_rendering_mode,               "screen_rendering_mode"
10607   },
10608   {
10609     TYPE_STRING,
10610     &setup.vsync_mode,                          "vsync_mode"
10611   },
10612   {
10613     TYPE_SWITCH,
10614     &setup.ask_on_escape,                       "ask_on_escape"
10615   },
10616   {
10617     TYPE_SWITCH,
10618     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10619   },
10620   {
10621     TYPE_SWITCH,
10622     &setup.ask_on_game_over,                    "ask_on_game_over"
10623   },
10624   {
10625     TYPE_SWITCH,
10626     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10627   },
10628   {
10629     TYPE_SWITCH,
10630     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10631   },
10632   {
10633     TYPE_SWITCH,
10634     &setup.quick_switch,                        "quick_player_switch"
10635   },
10636   {
10637     TYPE_SWITCH,
10638     &setup.input_on_focus,                      "input_on_focus"
10639   },
10640   {
10641     TYPE_SWITCH,
10642     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10643   },
10644   {
10645     TYPE_SWITCH,
10646     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10647   },
10648   {
10649     TYPE_SWITCH,
10650     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10651   },
10652   {
10653     TYPE_SWITCH,
10654     &setup.game_speed_extended,                 "game_speed_extended"
10655   },
10656   {
10657     TYPE_INTEGER,
10658     &setup.game_frame_delay,                    "game_frame_delay"
10659   },
10660   {
10661     TYPE_SWITCH,
10662     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10663   },
10664   {
10665     TYPE_SWITCH,
10666     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10667   },
10668   {
10669     TYPE_SWITCH,
10670     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10671   },
10672   {
10673     TYPE_SWITCH3,
10674     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10675   },
10676   {
10677     TYPE_SWITCH,
10678     &setup.sp_show_border_elements,             "sp_show_border_elements"
10679   },
10680   {
10681     TYPE_SWITCH,
10682     &setup.small_game_graphics,                 "small_game_graphics"
10683   },
10684   {
10685     TYPE_SWITCH,
10686     &setup.show_load_save_buttons,              "show_load_save_buttons"
10687   },
10688   {
10689     TYPE_SWITCH,
10690     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10691   },
10692   {
10693     TYPE_STRING,
10694     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10695   },
10696   {
10697     TYPE_STRING,
10698     &setup.graphics_set,                        "graphics_set"
10699   },
10700   {
10701     TYPE_STRING,
10702     &setup.sounds_set,                          "sounds_set"
10703   },
10704   {
10705     TYPE_STRING,
10706     &setup.music_set,                           "music_set"
10707   },
10708   {
10709     TYPE_SWITCH3,
10710     &setup.override_level_graphics,             "override_level_graphics"
10711   },
10712   {
10713     TYPE_SWITCH3,
10714     &setup.override_level_sounds,               "override_level_sounds"
10715   },
10716   {
10717     TYPE_SWITCH3,
10718     &setup.override_level_music,                "override_level_music"
10719   },
10720   {
10721     TYPE_INTEGER,
10722     &setup.volume_simple,                       "volume_simple"
10723   },
10724   {
10725     TYPE_INTEGER,
10726     &setup.volume_loops,                        "volume_loops"
10727   },
10728   {
10729     TYPE_INTEGER,
10730     &setup.volume_music,                        "volume_music"
10731   },
10732   {
10733     TYPE_SWITCH,
10734     &setup.network_mode,                        "network_mode"
10735   },
10736   {
10737     TYPE_PLAYER,
10738     &setup.network_player_nr,                   "network_player"
10739   },
10740   {
10741     TYPE_STRING,
10742     &setup.network_server_hostname,             "network_server_hostname"
10743   },
10744   {
10745     TYPE_STRING,
10746     &setup.touch.control_type,                  "touch.control_type"
10747   },
10748   {
10749     TYPE_INTEGER,
10750     &setup.touch.move_distance,                 "touch.move_distance"
10751   },
10752   {
10753     TYPE_INTEGER,
10754     &setup.touch.drop_distance,                 "touch.drop_distance"
10755   },
10756   {
10757     TYPE_INTEGER,
10758     &setup.touch.transparency,                  "touch.transparency"
10759   },
10760   {
10761     TYPE_INTEGER,
10762     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10763   },
10764   {
10765     TYPE_INTEGER,
10766     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10767   },
10768   {
10769     TYPE_INTEGER,
10770     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10771   },
10772   {
10773     TYPE_INTEGER,
10774     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10775   },
10776   {
10777     TYPE_INTEGER,
10778     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10779   },
10780   {
10781     TYPE_INTEGER,
10782     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10783   },
10784   {
10785     TYPE_SWITCH,
10786     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10787   },
10788 };
10789
10790 static struct TokenInfo auto_setup_tokens[] =
10791 {
10792   {
10793     TYPE_INTEGER,
10794     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10795   },
10796 };
10797
10798 static struct TokenInfo server_setup_tokens[] =
10799 {
10800   {
10801     TYPE_STRING,
10802     &setup.player_uuid,                         "player_uuid"
10803   },
10804   {
10805     TYPE_INTEGER,
10806     &setup.player_version,                      "player_version"
10807   },
10808   {
10809     TYPE_SWITCH,
10810     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10811   },
10812   {
10813     TYPE_STRING,
10814     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10815   },
10816   {
10817     TYPE_STRING,
10818     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10819   },
10820   {
10821     TYPE_SWITCH,
10822     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10823   },
10824   {
10825     TYPE_SWITCH,
10826     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10827   },
10828   {
10829     TYPE_SWITCH,
10830     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10831   },
10832   {
10833     TYPE_SWITCH,
10834     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10835   },
10836   {
10837     TYPE_SWITCH,
10838     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10839   },
10840 };
10841
10842 static struct TokenInfo editor_setup_tokens[] =
10843 {
10844   {
10845     TYPE_SWITCH,
10846     &setup.editor.el_classic,                   "editor.el_classic"
10847   },
10848   {
10849     TYPE_SWITCH,
10850     &setup.editor.el_custom,                    "editor.el_custom"
10851   },
10852   {
10853     TYPE_SWITCH,
10854     &setup.editor.el_user_defined,              "editor.el_user_defined"
10855   },
10856   {
10857     TYPE_SWITCH,
10858     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10859   },
10860   {
10861     TYPE_SWITCH,
10862     &setup.editor.el_headlines,                 "editor.el_headlines"
10863   },
10864   {
10865     TYPE_SWITCH,
10866     &setup.editor.show_element_token,           "editor.show_element_token"
10867   },
10868   {
10869     TYPE_SWITCH,
10870     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10871   },
10872 };
10873
10874 static struct TokenInfo editor_cascade_setup_tokens[] =
10875 {
10876   {
10877     TYPE_SWITCH,
10878     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10879   },
10880   {
10881     TYPE_SWITCH,
10882     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10883   },
10884   {
10885     TYPE_SWITCH,
10886     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10887   },
10888   {
10889     TYPE_SWITCH,
10890     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10891   },
10892   {
10893     TYPE_SWITCH,
10894     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10895   },
10896   {
10897     TYPE_SWITCH,
10898     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10899   },
10900   {
10901     TYPE_SWITCH,
10902     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10903   },
10904   {
10905     TYPE_SWITCH,
10906     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10907   },
10908   {
10909     TYPE_SWITCH,
10910     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10911   },
10912   {
10913     TYPE_SWITCH,
10914     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10915   },
10916   {
10917     TYPE_SWITCH,
10918     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10919   },
10920   {
10921     TYPE_SWITCH,
10922     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10923   },
10924   {
10925     TYPE_SWITCH,
10926     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10927   },
10928   {
10929     TYPE_SWITCH,
10930     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10931   },
10932   {
10933     TYPE_SWITCH,
10934     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10935   },
10936   {
10937     TYPE_SWITCH,
10938     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10939   },
10940   {
10941     TYPE_SWITCH,
10942     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10943   },
10944   {
10945     TYPE_SWITCH,
10946     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10947   },
10948   {
10949     TYPE_SWITCH,
10950     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10951   },
10952   {
10953     TYPE_SWITCH,
10954     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10955   },
10956 };
10957
10958 static struct TokenInfo shortcut_setup_tokens[] =
10959 {
10960   {
10961     TYPE_KEY_X11,
10962     &setup.shortcut.save_game,                  "shortcut.save_game"
10963   },
10964   {
10965     TYPE_KEY_X11,
10966     &setup.shortcut.load_game,                  "shortcut.load_game"
10967   },
10968   {
10969     TYPE_KEY_X11,
10970     &setup.shortcut.restart_game,               "shortcut.restart_game"
10971   },
10972   {
10973     TYPE_KEY_X11,
10974     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10975   },
10976   {
10977     TYPE_KEY_X11,
10978     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10979   },
10980   {
10981     TYPE_KEY_X11,
10982     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10983   },
10984   {
10985     TYPE_KEY_X11,
10986     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10987   },
10988   {
10989     TYPE_KEY_X11,
10990     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10991   },
10992   {
10993     TYPE_KEY_X11,
10994     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10995   },
10996   {
10997     TYPE_KEY_X11,
10998     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10999   },
11000   {
11001     TYPE_KEY_X11,
11002     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11003   },
11004   {
11005     TYPE_KEY_X11,
11006     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11007   },
11008   {
11009     TYPE_KEY_X11,
11010     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11011   },
11012   {
11013     TYPE_KEY_X11,
11014     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11015   },
11016   {
11017     TYPE_KEY_X11,
11018     &setup.shortcut.tape_record,                "shortcut.tape_record"
11019   },
11020   {
11021     TYPE_KEY_X11,
11022     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11023   },
11024   {
11025     TYPE_KEY_X11,
11026     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11027   },
11028   {
11029     TYPE_KEY_X11,
11030     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11031   },
11032   {
11033     TYPE_KEY_X11,
11034     &setup.shortcut.sound_music,                "shortcut.sound_music"
11035   },
11036   {
11037     TYPE_KEY_X11,
11038     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11039   },
11040   {
11041     TYPE_KEY_X11,
11042     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11043   },
11044   {
11045     TYPE_KEY_X11,
11046     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11047   },
11048   {
11049     TYPE_KEY_X11,
11050     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11051   },
11052 };
11053
11054 static struct SetupInputInfo setup_input;
11055 static struct TokenInfo player_setup_tokens[] =
11056 {
11057   {
11058     TYPE_BOOLEAN,
11059     &setup_input.use_joystick,                  ".use_joystick"
11060   },
11061   {
11062     TYPE_STRING,
11063     &setup_input.joy.device_name,               ".joy.device_name"
11064   },
11065   {
11066     TYPE_INTEGER,
11067     &setup_input.joy.xleft,                     ".joy.xleft"
11068   },
11069   {
11070     TYPE_INTEGER,
11071     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11072   },
11073   {
11074     TYPE_INTEGER,
11075     &setup_input.joy.xright,                    ".joy.xright"
11076   },
11077   {
11078     TYPE_INTEGER,
11079     &setup_input.joy.yupper,                    ".joy.yupper"
11080   },
11081   {
11082     TYPE_INTEGER,
11083     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11084   },
11085   {
11086     TYPE_INTEGER,
11087     &setup_input.joy.ylower,                    ".joy.ylower"
11088   },
11089   {
11090     TYPE_INTEGER,
11091     &setup_input.joy.snap,                      ".joy.snap_field"
11092   },
11093   {
11094     TYPE_INTEGER,
11095     &setup_input.joy.drop,                      ".joy.place_bomb"
11096   },
11097   {
11098     TYPE_KEY_X11,
11099     &setup_input.key.left,                      ".key.move_left"
11100   },
11101   {
11102     TYPE_KEY_X11,
11103     &setup_input.key.right,                     ".key.move_right"
11104   },
11105   {
11106     TYPE_KEY_X11,
11107     &setup_input.key.up,                        ".key.move_up"
11108   },
11109   {
11110     TYPE_KEY_X11,
11111     &setup_input.key.down,                      ".key.move_down"
11112   },
11113   {
11114     TYPE_KEY_X11,
11115     &setup_input.key.snap,                      ".key.snap_field"
11116   },
11117   {
11118     TYPE_KEY_X11,
11119     &setup_input.key.drop,                      ".key.place_bomb"
11120   },
11121 };
11122
11123 static struct TokenInfo system_setup_tokens[] =
11124 {
11125   {
11126     TYPE_STRING,
11127     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11128   },
11129   {
11130     TYPE_STRING,
11131     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11132   },
11133   {
11134     TYPE_STRING,
11135     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11136   },
11137   {
11138     TYPE_INTEGER,
11139     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11140   },
11141 };
11142
11143 static struct TokenInfo internal_setup_tokens[] =
11144 {
11145   {
11146     TYPE_STRING,
11147     &setup.internal.program_title,              "program_title"
11148   },
11149   {
11150     TYPE_STRING,
11151     &setup.internal.program_version,            "program_version"
11152   },
11153   {
11154     TYPE_STRING,
11155     &setup.internal.program_author,             "program_author"
11156   },
11157   {
11158     TYPE_STRING,
11159     &setup.internal.program_email,              "program_email"
11160   },
11161   {
11162     TYPE_STRING,
11163     &setup.internal.program_website,            "program_website"
11164   },
11165   {
11166     TYPE_STRING,
11167     &setup.internal.program_copyright,          "program_copyright"
11168   },
11169   {
11170     TYPE_STRING,
11171     &setup.internal.program_company,            "program_company"
11172   },
11173   {
11174     TYPE_STRING,
11175     &setup.internal.program_icon_file,          "program_icon_file"
11176   },
11177   {
11178     TYPE_STRING,
11179     &setup.internal.default_graphics_set,       "default_graphics_set"
11180   },
11181   {
11182     TYPE_STRING,
11183     &setup.internal.default_sounds_set,         "default_sounds_set"
11184   },
11185   {
11186     TYPE_STRING,
11187     &setup.internal.default_music_set,          "default_music_set"
11188   },
11189   {
11190     TYPE_STRING,
11191     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11192   },
11193   {
11194     TYPE_STRING,
11195     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11196   },
11197   {
11198     TYPE_STRING,
11199     &setup.internal.fallback_music_file,        "fallback_music_file"
11200   },
11201   {
11202     TYPE_STRING,
11203     &setup.internal.default_level_series,       "default_level_series"
11204   },
11205   {
11206     TYPE_INTEGER,
11207     &setup.internal.default_window_width,       "default_window_width"
11208   },
11209   {
11210     TYPE_INTEGER,
11211     &setup.internal.default_window_height,      "default_window_height"
11212   },
11213   {
11214     TYPE_BOOLEAN,
11215     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11216   },
11217   {
11218     TYPE_BOOLEAN,
11219     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11220   },
11221   {
11222     TYPE_BOOLEAN,
11223     &setup.internal.create_user_levelset,       "create_user_levelset"
11224   },
11225   {
11226     TYPE_BOOLEAN,
11227     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11228   },
11229   {
11230     TYPE_BOOLEAN,
11231     &setup.internal.menu_game,                  "menu_game"
11232   },
11233   {
11234     TYPE_BOOLEAN,
11235     &setup.internal.menu_engines,               "menu_engines"
11236   },
11237   {
11238     TYPE_BOOLEAN,
11239     &setup.internal.menu_editor,                "menu_editor"
11240   },
11241   {
11242     TYPE_BOOLEAN,
11243     &setup.internal.menu_graphics,              "menu_graphics"
11244   },
11245   {
11246     TYPE_BOOLEAN,
11247     &setup.internal.menu_sound,                 "menu_sound"
11248   },
11249   {
11250     TYPE_BOOLEAN,
11251     &setup.internal.menu_artwork,               "menu_artwork"
11252   },
11253   {
11254     TYPE_BOOLEAN,
11255     &setup.internal.menu_input,                 "menu_input"
11256   },
11257   {
11258     TYPE_BOOLEAN,
11259     &setup.internal.menu_touch,                 "menu_touch"
11260   },
11261   {
11262     TYPE_BOOLEAN,
11263     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11264   },
11265   {
11266     TYPE_BOOLEAN,
11267     &setup.internal.menu_exit,                  "menu_exit"
11268   },
11269   {
11270     TYPE_BOOLEAN,
11271     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11272   },
11273   {
11274     TYPE_BOOLEAN,
11275     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11276   },
11277   {
11278     TYPE_BOOLEAN,
11279     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11280   },
11281   {
11282     TYPE_BOOLEAN,
11283     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11284   },
11285   {
11286     TYPE_BOOLEAN,
11287     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11288   },
11289   {
11290     TYPE_BOOLEAN,
11291     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11292   },
11293   {
11294     TYPE_BOOLEAN,
11295     &setup.internal.info_title,                 "info_title"
11296   },
11297   {
11298     TYPE_BOOLEAN,
11299     &setup.internal.info_elements,              "info_elements"
11300   },
11301   {
11302     TYPE_BOOLEAN,
11303     &setup.internal.info_music,                 "info_music"
11304   },
11305   {
11306     TYPE_BOOLEAN,
11307     &setup.internal.info_credits,               "info_credits"
11308   },
11309   {
11310     TYPE_BOOLEAN,
11311     &setup.internal.info_program,               "info_program"
11312   },
11313   {
11314     TYPE_BOOLEAN,
11315     &setup.internal.info_version,               "info_version"
11316   },
11317   {
11318     TYPE_BOOLEAN,
11319     &setup.internal.info_levelset,              "info_levelset"
11320   },
11321   {
11322     TYPE_BOOLEAN,
11323     &setup.internal.info_exit,                  "info_exit"
11324   },
11325 };
11326
11327 static struct TokenInfo debug_setup_tokens[] =
11328 {
11329   {
11330     TYPE_INTEGER,
11331     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11332   },
11333   {
11334     TYPE_INTEGER,
11335     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11336   },
11337   {
11338     TYPE_INTEGER,
11339     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11340   },
11341   {
11342     TYPE_INTEGER,
11343     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11344   },
11345   {
11346     TYPE_INTEGER,
11347     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11348   },
11349   {
11350     TYPE_INTEGER,
11351     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11352   },
11353   {
11354     TYPE_INTEGER,
11355     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11356   },
11357   {
11358     TYPE_INTEGER,
11359     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11360   },
11361   {
11362     TYPE_INTEGER,
11363     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11364   },
11365   {
11366     TYPE_INTEGER,
11367     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11368   },
11369   {
11370     TYPE_KEY_X11,
11371     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11372   },
11373   {
11374     TYPE_KEY_X11,
11375     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11376   },
11377   {
11378     TYPE_KEY_X11,
11379     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11380   },
11381   {
11382     TYPE_KEY_X11,
11383     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11384   },
11385   {
11386     TYPE_KEY_X11,
11387     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11388   },
11389   {
11390     TYPE_KEY_X11,
11391     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11392   },
11393   {
11394     TYPE_KEY_X11,
11395     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11396   },
11397   {
11398     TYPE_KEY_X11,
11399     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11400   },
11401   {
11402     TYPE_KEY_X11,
11403     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11404   },
11405   {
11406     TYPE_KEY_X11,
11407     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11408   },
11409   {
11410     TYPE_BOOLEAN,
11411     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11412   {
11413     TYPE_BOOLEAN,
11414     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11415   },
11416   {
11417     TYPE_BOOLEAN,
11418     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11419   },
11420   {
11421     TYPE_SWITCH3,
11422     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11423   },
11424   {
11425     TYPE_INTEGER,
11426     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11427   },
11428 };
11429
11430 static struct TokenInfo options_setup_tokens[] =
11431 {
11432   {
11433     TYPE_BOOLEAN,
11434     &setup.options.verbose,                     "options.verbose"
11435   },
11436   {
11437     TYPE_BOOLEAN,
11438     &setup.options.debug,                       "options.debug"
11439   },
11440   {
11441     TYPE_STRING,
11442     &setup.options.debug_mode,                  "options.debug_mode"
11443   },
11444 };
11445
11446 static void setSetupInfoToDefaults(struct SetupInfo *si)
11447 {
11448   int i;
11449
11450   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11451
11452   si->multiple_users = TRUE;
11453
11454   si->sound = TRUE;
11455   si->sound_loops = TRUE;
11456   si->sound_music = TRUE;
11457   si->sound_simple = TRUE;
11458   si->toons = TRUE;
11459   si->global_animations = TRUE;
11460   si->scroll_delay = TRUE;
11461   si->forced_scroll_delay = FALSE;
11462   si->scroll_delay_value = STD_SCROLL_DELAY;
11463   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11464   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11465   si->fade_screens = TRUE;
11466   si->autorecord = TRUE;
11467   si->autorecord_after_replay = TRUE;
11468   si->auto_pause_on_start = FALSE;
11469   si->show_titlescreen = TRUE;
11470   si->quick_doors = FALSE;
11471   si->team_mode = FALSE;
11472   si->handicap = TRUE;
11473   si->skip_levels = TRUE;
11474   si->increment_levels = TRUE;
11475   si->auto_play_next_level = TRUE;
11476   si->count_score_after_game = TRUE;
11477   si->show_scores_after_game = TRUE;
11478   si->time_limit = TRUE;
11479   si->fullscreen = FALSE;
11480   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11481   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11482   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11483   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11484   si->ask_on_escape = TRUE;
11485   si->ask_on_escape_editor = TRUE;
11486   si->ask_on_game_over = TRUE;
11487   si->ask_on_quit_game = TRUE;
11488   si->ask_on_quit_program = TRUE;
11489   si->quick_switch = FALSE;
11490   si->input_on_focus = FALSE;
11491   si->prefer_aga_graphics = TRUE;
11492   si->prefer_lowpass_sounds = FALSE;
11493   si->prefer_extra_panel_items = TRUE;
11494   si->game_speed_extended = FALSE;
11495   si->game_frame_delay = GAME_FRAME_DELAY;
11496   si->bd_skip_uncovering = FALSE;
11497   si->bd_skip_hatching = FALSE;
11498   si->bd_scroll_delay = TRUE;
11499   si->bd_smooth_movements = AUTO;
11500   si->sp_show_border_elements = FALSE;
11501   si->small_game_graphics = FALSE;
11502   si->show_load_save_buttons = FALSE;
11503   si->show_undo_redo_buttons = FALSE;
11504   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11505
11506   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11507   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11508   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11509
11510   si->override_level_graphics = FALSE;
11511   si->override_level_sounds = FALSE;
11512   si->override_level_music = FALSE;
11513
11514   si->volume_simple = 100;              // percent
11515   si->volume_loops = 100;               // percent
11516   si->volume_music = 100;               // percent
11517
11518   si->network_mode = FALSE;
11519   si->network_player_nr = 0;            // first player
11520   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11521
11522   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11523   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11524   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11525   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11526   si->touch.draw_outlined = TRUE;
11527   si->touch.draw_pressed = TRUE;
11528
11529   for (i = 0; i < 2; i++)
11530   {
11531     char *default_grid_button[6][2] =
11532     {
11533       { "      ", "  ^^  " },
11534       { "      ", "  ^^  " },
11535       { "      ", "<<  >>" },
11536       { "      ", "<<  >>" },
11537       { "111222", "  vv  " },
11538       { "111222", "  vv  " }
11539     };
11540     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11541     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11542     int min_xsize = MIN(6, grid_xsize);
11543     int min_ysize = MIN(6, grid_ysize);
11544     int startx = grid_xsize - min_xsize;
11545     int starty = grid_ysize - min_ysize;
11546     int x, y;
11547
11548     // virtual buttons grid can only be set to defaults if video is initialized
11549     // (this will be repeated if virtual buttons are not loaded from setup file)
11550     if (video.initialized)
11551     {
11552       si->touch.grid_xsize[i] = grid_xsize;
11553       si->touch.grid_ysize[i] = grid_ysize;
11554     }
11555     else
11556     {
11557       si->touch.grid_xsize[i] = -1;
11558       si->touch.grid_ysize[i] = -1;
11559     }
11560
11561     for (x = 0; x < MAX_GRID_XSIZE; x++)
11562       for (y = 0; y < MAX_GRID_YSIZE; y++)
11563         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11564
11565     for (x = 0; x < min_xsize; x++)
11566       for (y = 0; y < min_ysize; y++)
11567         si->touch.grid_button[i][x][starty + y] =
11568           default_grid_button[y][0][x];
11569
11570     for (x = 0; x < min_xsize; x++)
11571       for (y = 0; y < min_ysize; y++)
11572         si->touch.grid_button[i][startx + x][starty + y] =
11573           default_grid_button[y][1][x];
11574   }
11575
11576   si->touch.grid_initialized            = video.initialized;
11577
11578   si->touch.overlay_buttons             = FALSE;
11579
11580   si->editor.el_boulderdash             = TRUE;
11581   si->editor.el_boulderdash_native      = TRUE;
11582   si->editor.el_boulderdash_effects     = TRUE;
11583   si->editor.el_emerald_mine            = TRUE;
11584   si->editor.el_emerald_mine_club       = TRUE;
11585   si->editor.el_more                    = TRUE;
11586   si->editor.el_sokoban                 = TRUE;
11587   si->editor.el_supaplex                = TRUE;
11588   si->editor.el_diamond_caves           = TRUE;
11589   si->editor.el_dx_boulderdash          = TRUE;
11590
11591   si->editor.el_mirror_magic            = TRUE;
11592   si->editor.el_deflektor               = TRUE;
11593
11594   si->editor.el_chars                   = TRUE;
11595   si->editor.el_steel_chars             = TRUE;
11596
11597   si->editor.el_classic                 = TRUE;
11598   si->editor.el_custom                  = TRUE;
11599
11600   si->editor.el_user_defined            = FALSE;
11601   si->editor.el_dynamic                 = TRUE;
11602
11603   si->editor.el_headlines               = TRUE;
11604
11605   si->editor.show_element_token         = FALSE;
11606
11607   si->editor.show_read_only_warning     = TRUE;
11608
11609   si->editor.use_template_for_new_levels = TRUE;
11610
11611   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11612   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11613   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11614   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11615   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11616
11617   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11618   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11619   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11620   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11621   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11622
11623   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11624   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11625   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11626   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11627   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11628   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11629
11630   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11631   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11632   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11633
11634   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11635   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11636   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11637   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11638
11639   for (i = 0; i < MAX_PLAYERS; i++)
11640   {
11641     si->input[i].use_joystick = FALSE;
11642     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11643     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11644     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11645     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11646     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11647     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11648     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11649     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11650     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11651     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11652     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11653     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11654     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11655     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11656     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11657   }
11658
11659   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11660   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11661   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11662   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11663
11664   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11665   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11666   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11667   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11668   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11669   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11670   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11671
11672   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11673
11674   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11675   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11676   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11677
11678   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11679   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11680   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11681
11682   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11683   si->internal.choose_from_top_leveldir = FALSE;
11684   si->internal.show_scaling_in_title = TRUE;
11685   si->internal.create_user_levelset = TRUE;
11686   si->internal.info_screens_from_main = FALSE;
11687
11688   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11689   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11690
11691   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11692   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11693   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11694   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11695   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11696   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11697   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11698   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11699   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11700   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11701
11702   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11703   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11704   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11705   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11706   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11707   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11708   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11709   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11710   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11711   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11712
11713   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11714   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11715
11716   si->debug.show_frames_per_second = FALSE;
11717
11718   si->debug.xsn_mode = AUTO;
11719   si->debug.xsn_percent = 0;
11720
11721   si->options.verbose = FALSE;
11722   si->options.debug = FALSE;
11723   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11724
11725 #if defined(PLATFORM_ANDROID)
11726   si->fullscreen = TRUE;
11727   si->touch.overlay_buttons = TRUE;
11728 #endif
11729
11730   setHideSetupEntry(&setup.debug.xsn_mode);
11731 }
11732
11733 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11734 {
11735   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11736 }
11737
11738 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11739 {
11740   si->player_uuid = NULL;       // (will be set later)
11741   si->player_version = 1;       // (will be set later)
11742
11743   si->use_api_server = TRUE;
11744   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11745   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11746   si->ask_for_uploading_tapes = TRUE;
11747   si->ask_for_remaining_tapes = FALSE;
11748   si->provide_uploading_tapes = TRUE;
11749   si->ask_for_using_api_server = TRUE;
11750   si->has_remaining_tapes = FALSE;
11751 }
11752
11753 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11754 {
11755   si->editor_cascade.el_bd              = TRUE;
11756   si->editor_cascade.el_bd_native       = TRUE;
11757   si->editor_cascade.el_bd_effects      = FALSE;
11758   si->editor_cascade.el_em              = TRUE;
11759   si->editor_cascade.el_emc             = TRUE;
11760   si->editor_cascade.el_rnd             = TRUE;
11761   si->editor_cascade.el_sb              = TRUE;
11762   si->editor_cascade.el_sp              = TRUE;
11763   si->editor_cascade.el_dc              = TRUE;
11764   si->editor_cascade.el_dx              = TRUE;
11765
11766   si->editor_cascade.el_mm              = TRUE;
11767   si->editor_cascade.el_df              = TRUE;
11768
11769   si->editor_cascade.el_chars           = FALSE;
11770   si->editor_cascade.el_steel_chars     = FALSE;
11771   si->editor_cascade.el_ce              = FALSE;
11772   si->editor_cascade.el_ge              = FALSE;
11773   si->editor_cascade.el_es              = FALSE;
11774   si->editor_cascade.el_ref             = FALSE;
11775   si->editor_cascade.el_user            = FALSE;
11776   si->editor_cascade.el_dynamic         = FALSE;
11777 }
11778
11779 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11780
11781 static char *getHideSetupToken(void *setup_value)
11782 {
11783   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11784
11785   if (setup_value != NULL)
11786     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11787
11788   return hide_setup_token;
11789 }
11790
11791 void setHideSetupEntry(void *setup_value)
11792 {
11793   char *hide_setup_token = getHideSetupToken(setup_value);
11794
11795   if (hide_setup_hash == NULL)
11796     hide_setup_hash = newSetupFileHash();
11797
11798   if (setup_value != NULL)
11799     setHashEntry(hide_setup_hash, hide_setup_token, "");
11800 }
11801
11802 void removeHideSetupEntry(void *setup_value)
11803 {
11804   char *hide_setup_token = getHideSetupToken(setup_value);
11805
11806   if (setup_value != NULL)
11807     removeHashEntry(hide_setup_hash, hide_setup_token);
11808 }
11809
11810 boolean hideSetupEntry(void *setup_value)
11811 {
11812   char *hide_setup_token = getHideSetupToken(setup_value);
11813
11814   return (setup_value != NULL &&
11815           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11816 }
11817
11818 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11819                                       struct TokenInfo *token_info,
11820                                       int token_nr, char *token_text)
11821 {
11822   char *token_hide_text = getStringCat2(token_text, ".hide");
11823   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11824
11825   // set the value of this setup option in the setup option structure
11826   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11827
11828   // check if this setup option should be hidden in the setup menu
11829   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11830     setHideSetupEntry(token_info[token_nr].value);
11831
11832   free(token_hide_text);
11833 }
11834
11835 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11836                                       struct TokenInfo *token_info,
11837                                       int token_nr)
11838 {
11839   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11840                             token_info[token_nr].text);
11841 }
11842
11843 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11844 {
11845   int i, pnr;
11846
11847   if (!setup_file_hash)
11848     return;
11849
11850   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11851     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11852
11853   setup.touch.grid_initialized = TRUE;
11854   for (i = 0; i < 2; i++)
11855   {
11856     int grid_xsize = setup.touch.grid_xsize[i];
11857     int grid_ysize = setup.touch.grid_ysize[i];
11858     int x, y;
11859
11860     // if virtual buttons are not loaded from setup file, repeat initializing
11861     // virtual buttons grid with default values later when video is initialized
11862     if (grid_xsize == -1 ||
11863         grid_ysize == -1)
11864     {
11865       setup.touch.grid_initialized = FALSE;
11866
11867       continue;
11868     }
11869
11870     for (y = 0; y < grid_ysize; y++)
11871     {
11872       char token_string[MAX_LINE_LEN];
11873
11874       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11875
11876       char *value_string = getHashEntry(setup_file_hash, token_string);
11877
11878       if (value_string == NULL)
11879         continue;
11880
11881       for (x = 0; x < grid_xsize; x++)
11882       {
11883         char c = value_string[x];
11884
11885         setup.touch.grid_button[i][x][y] =
11886           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11887       }
11888     }
11889   }
11890
11891   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11892     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11893
11894   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11895     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11896
11897   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11898   {
11899     char prefix[30];
11900
11901     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11902
11903     setup_input = setup.input[pnr];
11904     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11905     {
11906       char full_token[100];
11907
11908       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11909       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11910                                 full_token);
11911     }
11912     setup.input[pnr] = setup_input;
11913   }
11914
11915   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11916     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11917
11918   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11919     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11920
11921   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11922     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11923
11924   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11925     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11926
11927   setHideRelatedSetupEntries();
11928 }
11929
11930 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11931 {
11932   int i;
11933
11934   if (!setup_file_hash)
11935     return;
11936
11937   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11938     setSetupInfo(auto_setup_tokens, i,
11939                  getHashEntry(setup_file_hash,
11940                               auto_setup_tokens[i].text));
11941 }
11942
11943 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11944 {
11945   int i;
11946
11947   if (!setup_file_hash)
11948     return;
11949
11950   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11951     setSetupInfo(server_setup_tokens, i,
11952                  getHashEntry(setup_file_hash,
11953                               server_setup_tokens[i].text));
11954 }
11955
11956 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11957 {
11958   int i;
11959
11960   if (!setup_file_hash)
11961     return;
11962
11963   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11964     setSetupInfo(editor_cascade_setup_tokens, i,
11965                  getHashEntry(setup_file_hash,
11966                               editor_cascade_setup_tokens[i].text));
11967 }
11968
11969 void LoadUserNames(void)
11970 {
11971   int last_user_nr = user.nr;
11972   int i;
11973
11974   if (global.user_names != NULL)
11975   {
11976     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11977       checked_free(global.user_names[i]);
11978
11979     checked_free(global.user_names);
11980   }
11981
11982   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11983
11984   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11985   {
11986     user.nr = i;
11987
11988     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11989
11990     if (setup_file_hash)
11991     {
11992       char *player_name = getHashEntry(setup_file_hash, "player_name");
11993
11994       global.user_names[i] = getFixedUserName(player_name);
11995
11996       freeSetupFileHash(setup_file_hash);
11997     }
11998
11999     if (global.user_names[i] == NULL)
12000       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12001   }
12002
12003   user.nr = last_user_nr;
12004 }
12005
12006 void LoadSetupFromFilename(char *filename)
12007 {
12008   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12009
12010   if (setup_file_hash)
12011   {
12012     decodeSetupFileHash_Default(setup_file_hash);
12013
12014     freeSetupFileHash(setup_file_hash);
12015   }
12016   else
12017   {
12018     Debug("setup", "using default setup values");
12019   }
12020 }
12021
12022 static void LoadSetup_SpecialPostProcessing(void)
12023 {
12024   char *player_name_new;
12025
12026   // needed to work around problems with fixed length strings
12027   player_name_new = getFixedUserName(setup.player_name);
12028   free(setup.player_name);
12029   setup.player_name = player_name_new;
12030
12031   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12032   if (setup.scroll_delay == FALSE)
12033   {
12034     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12035     setup.scroll_delay = TRUE;                  // now always "on"
12036   }
12037
12038   // make sure that scroll delay value stays inside valid range
12039   setup.scroll_delay_value =
12040     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12041 }
12042
12043 void LoadSetup_Default(void)
12044 {
12045   char *filename;
12046
12047   // always start with reliable default values
12048   setSetupInfoToDefaults(&setup);
12049
12050   // try to load setup values from default setup file
12051   filename = getDefaultSetupFilename();
12052
12053   if (fileExists(filename))
12054     LoadSetupFromFilename(filename);
12055
12056   // try to load setup values from platform setup file
12057   filename = getPlatformSetupFilename();
12058
12059   if (fileExists(filename))
12060     LoadSetupFromFilename(filename);
12061
12062   // try to load setup values from user setup file
12063   filename = getSetupFilename();
12064
12065   LoadSetupFromFilename(filename);
12066
12067   LoadSetup_SpecialPostProcessing();
12068 }
12069
12070 void LoadSetup_AutoSetup(void)
12071 {
12072   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12073   SetupFileHash *setup_file_hash = NULL;
12074
12075   // always start with reliable default values
12076   setSetupInfoToDefaults_AutoSetup(&setup);
12077
12078   setup_file_hash = loadSetupFileHash(filename);
12079
12080   if (setup_file_hash)
12081   {
12082     decodeSetupFileHash_AutoSetup(setup_file_hash);
12083
12084     freeSetupFileHash(setup_file_hash);
12085   }
12086
12087   free(filename);
12088 }
12089
12090 void LoadSetup_ServerSetup(void)
12091 {
12092   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12093   SetupFileHash *setup_file_hash = NULL;
12094
12095   // always start with reliable default values
12096   setSetupInfoToDefaults_ServerSetup(&setup);
12097
12098   setup_file_hash = loadSetupFileHash(filename);
12099
12100   if (setup_file_hash)
12101   {
12102     decodeSetupFileHash_ServerSetup(setup_file_hash);
12103
12104     freeSetupFileHash(setup_file_hash);
12105   }
12106
12107   free(filename);
12108
12109   if (setup.player_uuid == NULL)
12110   {
12111     // player UUID does not yet exist in setup file
12112     setup.player_uuid = getStringCopy(getUUID());
12113     setup.player_version = 2;
12114
12115     SaveSetup_ServerSetup();
12116   }
12117 }
12118
12119 void LoadSetup_EditorCascade(void)
12120 {
12121   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12122   SetupFileHash *setup_file_hash = NULL;
12123
12124   // always start with reliable default values
12125   setSetupInfoToDefaults_EditorCascade(&setup);
12126
12127   setup_file_hash = loadSetupFileHash(filename);
12128
12129   if (setup_file_hash)
12130   {
12131     decodeSetupFileHash_EditorCascade(setup_file_hash);
12132
12133     freeSetupFileHash(setup_file_hash);
12134   }
12135
12136   free(filename);
12137 }
12138
12139 void LoadSetup(void)
12140 {
12141   LoadSetup_Default();
12142   LoadSetup_AutoSetup();
12143   LoadSetup_ServerSetup();
12144   LoadSetup_EditorCascade();
12145 }
12146
12147 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12148                                            char *mapping_line)
12149 {
12150   char mapping_guid[MAX_LINE_LEN];
12151   char *mapping_start, *mapping_end;
12152
12153   // get GUID from game controller mapping line: copy complete line
12154   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12155   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12156
12157   // get GUID from game controller mapping line: cut after GUID part
12158   mapping_start = strchr(mapping_guid, ',');
12159   if (mapping_start != NULL)
12160     *mapping_start = '\0';
12161
12162   // cut newline from game controller mapping line
12163   mapping_end = strchr(mapping_line, '\n');
12164   if (mapping_end != NULL)
12165     *mapping_end = '\0';
12166
12167   // add mapping entry to game controller mappings hash
12168   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12169 }
12170
12171 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12172                                                  char *filename)
12173 {
12174   FILE *file;
12175
12176   if (!(file = fopen(filename, MODE_READ)))
12177   {
12178     Warn("cannot read game controller mappings file '%s'", filename);
12179
12180     return;
12181   }
12182
12183   while (!feof(file))
12184   {
12185     char line[MAX_LINE_LEN];
12186
12187     if (!fgets(line, MAX_LINE_LEN, file))
12188       break;
12189
12190     addGameControllerMappingToHash(mappings_hash, line);
12191   }
12192
12193   fclose(file);
12194 }
12195
12196 void SaveSetup_Default(void)
12197 {
12198   char *filename = getSetupFilename();
12199   FILE *file;
12200   int i, pnr;
12201
12202   InitUserDataDirectory();
12203
12204   if (!(file = fopen(filename, MODE_WRITE)))
12205   {
12206     Warn("cannot write setup file '%s'", filename);
12207
12208     return;
12209   }
12210
12211   fprintFileHeader(file, SETUP_FILENAME);
12212
12213   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12214   {
12215     // just to make things nicer :)
12216     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12217         global_setup_tokens[i].value == &setup.sound                    ||
12218         global_setup_tokens[i].value == &setup.graphics_set             ||
12219         global_setup_tokens[i].value == &setup.volume_simple            ||
12220         global_setup_tokens[i].value == &setup.network_mode             ||
12221         global_setup_tokens[i].value == &setup.touch.control_type       ||
12222         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12223         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12224       fprintf(file, "\n");
12225
12226     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12227   }
12228
12229   for (i = 0; i < 2; i++)
12230   {
12231     int grid_xsize = setup.touch.grid_xsize[i];
12232     int grid_ysize = setup.touch.grid_ysize[i];
12233     int x, y;
12234
12235     fprintf(file, "\n");
12236
12237     for (y = 0; y < grid_ysize; y++)
12238     {
12239       char token_string[MAX_LINE_LEN];
12240       char value_string[MAX_LINE_LEN];
12241
12242       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12243
12244       for (x = 0; x < grid_xsize; x++)
12245       {
12246         char c = setup.touch.grid_button[i][x][y];
12247
12248         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12249       }
12250
12251       value_string[grid_xsize] = '\0';
12252
12253       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12254     }
12255   }
12256
12257   fprintf(file, "\n");
12258   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12259     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12260
12261   fprintf(file, "\n");
12262   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12263     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12264
12265   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12266   {
12267     char prefix[30];
12268
12269     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12270     fprintf(file, "\n");
12271
12272     setup_input = setup.input[pnr];
12273     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12274       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12275   }
12276
12277   fprintf(file, "\n");
12278   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12279     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12280
12281   // (internal setup values not saved to user setup file)
12282
12283   fprintf(file, "\n");
12284   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12285     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12286         setup.debug.xsn_mode != AUTO)
12287       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12288
12289   fprintf(file, "\n");
12290   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12291     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12292
12293   fclose(file);
12294
12295   SetFilePermissions(filename, PERMS_PRIVATE);
12296 }
12297
12298 void SaveSetup_AutoSetup(void)
12299 {
12300   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12301   FILE *file;
12302   int i;
12303
12304   InitUserDataDirectory();
12305
12306   if (!(file = fopen(filename, MODE_WRITE)))
12307   {
12308     Warn("cannot write auto setup file '%s'", filename);
12309
12310     free(filename);
12311
12312     return;
12313   }
12314
12315   fprintFileHeader(file, AUTOSETUP_FILENAME);
12316
12317   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12318     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12319
12320   fclose(file);
12321
12322   SetFilePermissions(filename, PERMS_PRIVATE);
12323
12324   free(filename);
12325 }
12326
12327 void SaveSetup_ServerSetup(void)
12328 {
12329   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12330   FILE *file;
12331   int i;
12332
12333   InitUserDataDirectory();
12334
12335   if (!(file = fopen(filename, MODE_WRITE)))
12336   {
12337     Warn("cannot write server setup file '%s'", filename);
12338
12339     free(filename);
12340
12341     return;
12342   }
12343
12344   fprintFileHeader(file, SERVERSETUP_FILENAME);
12345
12346   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12347   {
12348     // just to make things nicer :)
12349     if (server_setup_tokens[i].value == &setup.use_api_server)
12350       fprintf(file, "\n");
12351
12352     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12353   }
12354
12355   fclose(file);
12356
12357   SetFilePermissions(filename, PERMS_PRIVATE);
12358
12359   free(filename);
12360 }
12361
12362 void SaveSetup_EditorCascade(void)
12363 {
12364   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12365   FILE *file;
12366   int i;
12367
12368   InitUserDataDirectory();
12369
12370   if (!(file = fopen(filename, MODE_WRITE)))
12371   {
12372     Warn("cannot write editor cascade state file '%s'", filename);
12373
12374     free(filename);
12375
12376     return;
12377   }
12378
12379   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12380
12381   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12382     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12383
12384   fclose(file);
12385
12386   SetFilePermissions(filename, PERMS_PRIVATE);
12387
12388   free(filename);
12389 }
12390
12391 void SaveSetup(void)
12392 {
12393   SaveSetup_Default();
12394   SaveSetup_AutoSetup();
12395   SaveSetup_ServerSetup();
12396   SaveSetup_EditorCascade();
12397 }
12398
12399 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12400                                                   char *filename)
12401 {
12402   FILE *file;
12403
12404   if (!(file = fopen(filename, MODE_WRITE)))
12405   {
12406     Warn("cannot write game controller mappings file '%s'", filename);
12407
12408     return;
12409   }
12410
12411   BEGIN_HASH_ITERATION(mappings_hash, itr)
12412   {
12413     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12414   }
12415   END_HASH_ITERATION(mappings_hash, itr)
12416
12417   fclose(file);
12418 }
12419
12420 void SaveSetup_AddGameControllerMapping(char *mapping)
12421 {
12422   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12423   SetupFileHash *mappings_hash = newSetupFileHash();
12424
12425   InitUserDataDirectory();
12426
12427   // load existing personal game controller mappings
12428   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12429
12430   // add new mapping to personal game controller mappings
12431   addGameControllerMappingToHash(mappings_hash, mapping);
12432
12433   // save updated personal game controller mappings
12434   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12435
12436   freeSetupFileHash(mappings_hash);
12437   free(filename);
12438 }
12439
12440 void LoadCustomElementDescriptions(void)
12441 {
12442   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12443   SetupFileHash *setup_file_hash;
12444   int i;
12445
12446   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12447   {
12448     if (element_info[i].custom_description != NULL)
12449     {
12450       free(element_info[i].custom_description);
12451       element_info[i].custom_description = NULL;
12452     }
12453   }
12454
12455   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12456     return;
12457
12458   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12459   {
12460     char *token = getStringCat2(element_info[i].token_name, ".name");
12461     char *value = getHashEntry(setup_file_hash, token);
12462
12463     if (value != NULL)
12464       element_info[i].custom_description = getStringCopy(value);
12465
12466     free(token);
12467   }
12468
12469   freeSetupFileHash(setup_file_hash);
12470 }
12471
12472 static int getElementFromToken(char *token)
12473 {
12474   char *value = getHashEntry(element_token_hash, token);
12475
12476   if (value != NULL)
12477     return atoi(value);
12478
12479   Warn("unknown element token '%s'", token);
12480
12481   return EL_UNDEFINED;
12482 }
12483
12484 void FreeGlobalAnimEventInfo(void)
12485 {
12486   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12487
12488   if (gaei->event_list == NULL)
12489     return;
12490
12491   int i;
12492
12493   for (i = 0; i < gaei->num_event_lists; i++)
12494   {
12495     checked_free(gaei->event_list[i]->event_value);
12496     checked_free(gaei->event_list[i]);
12497   }
12498
12499   checked_free(gaei->event_list);
12500
12501   gaei->event_list = NULL;
12502   gaei->num_event_lists = 0;
12503 }
12504
12505 static int AddGlobalAnimEventList(void)
12506 {
12507   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12508   int list_pos = gaei->num_event_lists++;
12509
12510   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12511                                      sizeof(struct GlobalAnimEventListInfo *));
12512
12513   gaei->event_list[list_pos] =
12514     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12515
12516   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12517
12518   gaeli->event_value = NULL;
12519   gaeli->num_event_values = 0;
12520
12521   return list_pos;
12522 }
12523
12524 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12525 {
12526   // do not add empty global animation events
12527   if (event_value == ANIM_EVENT_NONE)
12528     return list_pos;
12529
12530   // if list position is undefined, create new list
12531   if (list_pos == ANIM_EVENT_UNDEFINED)
12532     list_pos = AddGlobalAnimEventList();
12533
12534   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12535   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12536   int value_pos = gaeli->num_event_values++;
12537
12538   gaeli->event_value = checked_realloc(gaeli->event_value,
12539                                        gaeli->num_event_values * sizeof(int *));
12540
12541   gaeli->event_value[value_pos] = event_value;
12542
12543   return list_pos;
12544 }
12545
12546 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12547 {
12548   if (list_pos == ANIM_EVENT_UNDEFINED)
12549     return ANIM_EVENT_NONE;
12550
12551   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12552   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12553
12554   return gaeli->event_value[value_pos];
12555 }
12556
12557 int GetGlobalAnimEventValueCount(int list_pos)
12558 {
12559   if (list_pos == ANIM_EVENT_UNDEFINED)
12560     return 0;
12561
12562   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12563   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12564
12565   return gaeli->num_event_values;
12566 }
12567
12568 // This function checks if a string <s> of the format "string1, string2, ..."
12569 // exactly contains a string <s_contained>.
12570
12571 static boolean string_has_parameter(char *s, char *s_contained)
12572 {
12573   char *substring;
12574
12575   if (s == NULL || s_contained == NULL)
12576     return FALSE;
12577
12578   if (strlen(s_contained) > strlen(s))
12579     return FALSE;
12580
12581   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12582   {
12583     char next_char = s[strlen(s_contained)];
12584
12585     // check if next character is delimiter or whitespace
12586     if (next_char == ',' || next_char == '\0' ||
12587         next_char == ' ' || next_char == '\t')
12588       return TRUE;
12589   }
12590
12591   // check if string contains another parameter string after a comma
12592   substring = strchr(s, ',');
12593   if (substring == NULL)        // string does not contain a comma
12594     return FALSE;
12595
12596   // advance string pointer to next character after the comma
12597   substring++;
12598
12599   // skip potential whitespaces after the comma
12600   while (*substring == ' ' || *substring == '\t')
12601     substring++;
12602
12603   return string_has_parameter(substring, s_contained);
12604 }
12605
12606 static int get_anim_parameter_value_ce(char *s)
12607 {
12608   char *s_ptr = s;
12609   char *pattern_1 = "ce_change:custom_";
12610   char *pattern_2 = ".page_";
12611   int pattern_1_len = strlen(pattern_1);
12612   char *matching_char = strstr(s_ptr, pattern_1);
12613   int result = ANIM_EVENT_NONE;
12614
12615   if (matching_char == NULL)
12616     return ANIM_EVENT_NONE;
12617
12618   result = ANIM_EVENT_CE_CHANGE;
12619
12620   s_ptr = matching_char + pattern_1_len;
12621
12622   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12623   if (*s_ptr >= '0' && *s_ptr <= '9')
12624   {
12625     int gic_ce_nr = (*s_ptr++ - '0');
12626
12627     if (*s_ptr >= '0' && *s_ptr <= '9')
12628     {
12629       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12630
12631       if (*s_ptr >= '0' && *s_ptr <= '9')
12632         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12633     }
12634
12635     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12636       return ANIM_EVENT_NONE;
12637
12638     // custom element stored as 0 to 255
12639     gic_ce_nr--;
12640
12641     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12642   }
12643   else
12644   {
12645     // invalid custom element number specified
12646
12647     return ANIM_EVENT_NONE;
12648   }
12649
12650   // check for change page number ("page_X" or "page_XX") (optional)
12651   if (strPrefix(s_ptr, pattern_2))
12652   {
12653     s_ptr += strlen(pattern_2);
12654
12655     if (*s_ptr >= '0' && *s_ptr <= '9')
12656     {
12657       int gic_page_nr = (*s_ptr++ - '0');
12658
12659       if (*s_ptr >= '0' && *s_ptr <= '9')
12660         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12661
12662       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12663         return ANIM_EVENT_NONE;
12664
12665       // change page stored as 1 to 32 (0 means "all change pages")
12666
12667       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12668     }
12669     else
12670     {
12671       // invalid animation part number specified
12672
12673       return ANIM_EVENT_NONE;
12674     }
12675   }
12676
12677   // discard result if next character is neither delimiter nor whitespace
12678   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12679         *s_ptr == ' ' || *s_ptr == '\t'))
12680     return ANIM_EVENT_NONE;
12681
12682   return result;
12683 }
12684
12685 static int get_anim_parameter_value(char *s)
12686 {
12687   int event_value[] =
12688   {
12689     ANIM_EVENT_CLICK,
12690     ANIM_EVENT_INIT,
12691     ANIM_EVENT_START,
12692     ANIM_EVENT_END,
12693     ANIM_EVENT_POST
12694   };
12695   char *pattern_1[] =
12696   {
12697     "click:anim_",
12698     "init:anim_",
12699     "start:anim_",
12700     "end:anim_",
12701     "post:anim_"
12702   };
12703   char *pattern_2 = ".part_";
12704   char *matching_char = NULL;
12705   char *s_ptr = s;
12706   int pattern_1_len = 0;
12707   int result = ANIM_EVENT_NONE;
12708   int i;
12709
12710   result = get_anim_parameter_value_ce(s);
12711
12712   if (result != ANIM_EVENT_NONE)
12713     return result;
12714
12715   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12716   {
12717     matching_char = strstr(s_ptr, pattern_1[i]);
12718     pattern_1_len = strlen(pattern_1[i]);
12719     result = event_value[i];
12720
12721     if (matching_char != NULL)
12722       break;
12723   }
12724
12725   if (matching_char == NULL)
12726     return ANIM_EVENT_NONE;
12727
12728   s_ptr = matching_char + pattern_1_len;
12729
12730   // check for main animation number ("anim_X" or "anim_XX")
12731   if (*s_ptr >= '0' && *s_ptr <= '9')
12732   {
12733     int gic_anim_nr = (*s_ptr++ - '0');
12734
12735     if (*s_ptr >= '0' && *s_ptr <= '9')
12736       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12737
12738     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12739       return ANIM_EVENT_NONE;
12740
12741     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12742   }
12743   else
12744   {
12745     // invalid main animation number specified
12746
12747     return ANIM_EVENT_NONE;
12748   }
12749
12750   // check for animation part number ("part_X" or "part_XX") (optional)
12751   if (strPrefix(s_ptr, pattern_2))
12752   {
12753     s_ptr += strlen(pattern_2);
12754
12755     if (*s_ptr >= '0' && *s_ptr <= '9')
12756     {
12757       int gic_part_nr = (*s_ptr++ - '0');
12758
12759       if (*s_ptr >= '0' && *s_ptr <= '9')
12760         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12761
12762       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12763         return ANIM_EVENT_NONE;
12764
12765       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12766     }
12767     else
12768     {
12769       // invalid animation part number specified
12770
12771       return ANIM_EVENT_NONE;
12772     }
12773   }
12774
12775   // discard result if next character is neither delimiter nor whitespace
12776   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12777         *s_ptr == ' ' || *s_ptr == '\t'))
12778     return ANIM_EVENT_NONE;
12779
12780   return result;
12781 }
12782
12783 static int get_anim_parameter_values(char *s)
12784 {
12785   int list_pos = ANIM_EVENT_UNDEFINED;
12786   int event_value = ANIM_EVENT_DEFAULT;
12787
12788   if (string_has_parameter(s, "any"))
12789     event_value |= ANIM_EVENT_ANY;
12790
12791   if (string_has_parameter(s, "click:self") ||
12792       string_has_parameter(s, "click") ||
12793       string_has_parameter(s, "self"))
12794     event_value |= ANIM_EVENT_SELF;
12795
12796   if (string_has_parameter(s, "unclick:any"))
12797     event_value |= ANIM_EVENT_UNCLICK_ANY;
12798
12799   // if animation event found, add it to global animation event list
12800   if (event_value != ANIM_EVENT_NONE)
12801     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12802
12803   while (s != NULL)
12804   {
12805     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12806     event_value = get_anim_parameter_value(s);
12807
12808     // if animation event found, add it to global animation event list
12809     if (event_value != ANIM_EVENT_NONE)
12810       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12811
12812     // continue with next part of the string, starting with next comma
12813     s = strchr(s + 1, ',');
12814   }
12815
12816   return list_pos;
12817 }
12818
12819 static int get_anim_action_parameter_value(char *token)
12820 {
12821   // check most common default case first to massively speed things up
12822   if (strEqual(token, ARG_UNDEFINED))
12823     return ANIM_EVENT_ACTION_NONE;
12824
12825   int result = getImageIDFromToken(token);
12826
12827   if (result == -1)
12828   {
12829     char *gfx_token = getStringCat2("gfx.", token);
12830
12831     result = getImageIDFromToken(gfx_token);
12832
12833     checked_free(gfx_token);
12834   }
12835
12836   if (result == -1)
12837   {
12838     Key key = getKeyFromX11KeyName(token);
12839
12840     if (key != KSYM_UNDEFINED)
12841       result = -(int)key;
12842   }
12843
12844   if (result == -1)
12845   {
12846     if (isURL(token))
12847     {
12848       result = get_hash_from_string(token);     // unsigned int => int
12849       result = ABS(result);                     // may be negative now
12850       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12851
12852       setHashEntry(anim_url_hash, int2str(result, 0), token);
12853     }
12854   }
12855
12856   if (result == -1)
12857     result = ANIM_EVENT_ACTION_NONE;
12858
12859   return result;
12860 }
12861
12862 int get_parameter_value(char *value_raw, char *suffix, int type)
12863 {
12864   char *value = getStringToLower(value_raw);
12865   int result = 0;       // probably a save default value
12866
12867   if (strEqual(suffix, ".direction"))
12868   {
12869     result = (strEqual(value, "left")  ? MV_LEFT :
12870               strEqual(value, "right") ? MV_RIGHT :
12871               strEqual(value, "up")    ? MV_UP :
12872               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12873   }
12874   else if (strEqual(suffix, ".position"))
12875   {
12876     result = (strEqual(value, "left")   ? POS_LEFT :
12877               strEqual(value, "right")  ? POS_RIGHT :
12878               strEqual(value, "top")    ? POS_TOP :
12879               strEqual(value, "upper")  ? POS_UPPER :
12880               strEqual(value, "middle") ? POS_MIDDLE :
12881               strEqual(value, "lower")  ? POS_LOWER :
12882               strEqual(value, "bottom") ? POS_BOTTOM :
12883               strEqual(value, "any")    ? POS_ANY :
12884               strEqual(value, "ce")     ? POS_CE :
12885               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12886               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12887   }
12888   else if (strEqual(suffix, ".align"))
12889   {
12890     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12891               strEqual(value, "right")  ? ALIGN_RIGHT :
12892               strEqual(value, "center") ? ALIGN_CENTER :
12893               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12894   }
12895   else if (strEqual(suffix, ".valign"))
12896   {
12897     result = (strEqual(value, "top")    ? VALIGN_TOP :
12898               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12899               strEqual(value, "middle") ? VALIGN_MIDDLE :
12900               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12901   }
12902   else if (strEqual(suffix, ".anim_mode"))
12903   {
12904     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12905               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12906               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12907               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12908               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12909               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12910               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12911               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12912               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12913               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12914               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12915               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12916               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12917               string_has_parameter(value, "all")        ? ANIM_ALL :
12918               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12919               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12920               ANIM_DEFAULT);
12921
12922     if (string_has_parameter(value, "once"))
12923       result |= ANIM_ONCE;
12924
12925     if (string_has_parameter(value, "reverse"))
12926       result |= ANIM_REVERSE;
12927
12928     if (string_has_parameter(value, "opaque_player"))
12929       result |= ANIM_OPAQUE_PLAYER;
12930
12931     if (string_has_parameter(value, "static_panel"))
12932       result |= ANIM_STATIC_PANEL;
12933   }
12934   else if (strEqual(suffix, ".init_event") ||
12935            strEqual(suffix, ".anim_event"))
12936   {
12937     result = get_anim_parameter_values(value);
12938   }
12939   else if (strEqual(suffix, ".init_delay_action") ||
12940            strEqual(suffix, ".anim_delay_action") ||
12941            strEqual(suffix, ".post_delay_action") ||
12942            strEqual(suffix, ".init_event_action") ||
12943            strEqual(suffix, ".anim_event_action"))
12944   {
12945     result = get_anim_action_parameter_value(value_raw);
12946   }
12947   else if (strEqual(suffix, ".class"))
12948   {
12949     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12950               get_hash_from_string(value));
12951   }
12952   else if (strEqual(suffix, ".style"))
12953   {
12954     result = STYLE_DEFAULT;
12955
12956     if (string_has_parameter(value, "accurate_borders"))
12957       result |= STYLE_ACCURATE_BORDERS;
12958
12959     if (string_has_parameter(value, "inner_corners"))
12960       result |= STYLE_INNER_CORNERS;
12961
12962     if (string_has_parameter(value, "reverse"))
12963       result |= STYLE_REVERSE;
12964
12965     if (string_has_parameter(value, "leftmost_position"))
12966       result |= STYLE_LEFTMOST_POSITION;
12967
12968     if (string_has_parameter(value, "block_clicks"))
12969       result |= STYLE_BLOCK;
12970
12971     if (string_has_parameter(value, "passthrough_clicks"))
12972       result |= STYLE_PASSTHROUGH;
12973
12974     if (string_has_parameter(value, "multiple_actions"))
12975       result |= STYLE_MULTIPLE_ACTIONS;
12976
12977     if (string_has_parameter(value, "consume_ce_event"))
12978       result |= STYLE_CONSUME_CE_EVENT;
12979   }
12980   else if (strEqual(suffix, ".fade_mode"))
12981   {
12982     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12983               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12984               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12985               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12986               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12987               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12988               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12989               FADE_MODE_DEFAULT);
12990   }
12991   else if (strEqual(suffix, ".auto_delay_unit"))
12992   {
12993     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12994               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12995               AUTO_DELAY_UNIT_DEFAULT);
12996   }
12997   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12998   {
12999     result = gfx.get_font_from_token_function(value);
13000   }
13001   else          // generic parameter of type integer or boolean
13002   {
13003     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13004               type == TYPE_INTEGER ? get_integer_from_string(value) :
13005               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13006               ARG_UNDEFINED_VALUE);
13007   }
13008
13009   free(value);
13010
13011   return result;
13012 }
13013
13014 static int get_token_parameter_value(char *token, char *value_raw)
13015 {
13016   char *suffix;
13017
13018   if (token == NULL || value_raw == NULL)
13019     return ARG_UNDEFINED_VALUE;
13020
13021   suffix = strrchr(token, '.');
13022   if (suffix == NULL)
13023     suffix = token;
13024
13025   if (strEqual(suffix, ".element"))
13026     return getElementFromToken(value_raw);
13027
13028   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13029   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13030 }
13031
13032 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13033                                      boolean ignore_defaults)
13034 {
13035   int i;
13036
13037   for (i = 0; image_config_vars[i].token != NULL; i++)
13038   {
13039     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13040
13041     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13042     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13043       continue;
13044
13045     if (value != NULL)
13046       *image_config_vars[i].value =
13047         get_token_parameter_value(image_config_vars[i].token, value);
13048   }
13049 }
13050
13051 void InitMenuDesignSettings_Static(void)
13052 {
13053   // always start with reliable default values from static default config
13054   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13055 }
13056
13057 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13058 {
13059   int i;
13060
13061   // the following initializes hierarchical values from static configuration
13062
13063   // special case: initialize "ARG_DEFAULT" values in static default config
13064   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13065   titlescreen_initial_first_default.fade_mode  =
13066     title_initial_first_default.fade_mode;
13067   titlescreen_initial_first_default.fade_delay =
13068     title_initial_first_default.fade_delay;
13069   titlescreen_initial_first_default.post_delay =
13070     title_initial_first_default.post_delay;
13071   titlescreen_initial_first_default.auto_delay =
13072     title_initial_first_default.auto_delay;
13073   titlescreen_initial_first_default.auto_delay_unit =
13074     title_initial_first_default.auto_delay_unit;
13075   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13076   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13077   titlescreen_first_default.post_delay = title_first_default.post_delay;
13078   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13079   titlescreen_first_default.auto_delay_unit =
13080     title_first_default.auto_delay_unit;
13081   titlemessage_initial_first_default.fade_mode  =
13082     title_initial_first_default.fade_mode;
13083   titlemessage_initial_first_default.fade_delay =
13084     title_initial_first_default.fade_delay;
13085   titlemessage_initial_first_default.post_delay =
13086     title_initial_first_default.post_delay;
13087   titlemessage_initial_first_default.auto_delay =
13088     title_initial_first_default.auto_delay;
13089   titlemessage_initial_first_default.auto_delay_unit =
13090     title_initial_first_default.auto_delay_unit;
13091   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13092   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13093   titlemessage_first_default.post_delay = title_first_default.post_delay;
13094   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13095   titlemessage_first_default.auto_delay_unit =
13096     title_first_default.auto_delay_unit;
13097
13098   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13099   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13100   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13101   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13102   titlescreen_initial_default.auto_delay_unit =
13103     title_initial_default.auto_delay_unit;
13104   titlescreen_default.fade_mode  = title_default.fade_mode;
13105   titlescreen_default.fade_delay = title_default.fade_delay;
13106   titlescreen_default.post_delay = title_default.post_delay;
13107   titlescreen_default.auto_delay = title_default.auto_delay;
13108   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13109   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13110   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13111   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13112   titlemessage_initial_default.auto_delay_unit =
13113     title_initial_default.auto_delay_unit;
13114   titlemessage_default.fade_mode  = title_default.fade_mode;
13115   titlemessage_default.fade_delay = title_default.fade_delay;
13116   titlemessage_default.post_delay = title_default.post_delay;
13117   titlemessage_default.auto_delay = title_default.auto_delay;
13118   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13119
13120   // special case: initialize "ARG_DEFAULT" values in static default config
13121   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13122   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13123   {
13124     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13125     titlescreen_first[i] = titlescreen_first_default;
13126     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13127     titlemessage_first[i] = titlemessage_first_default;
13128
13129     titlescreen_initial[i] = titlescreen_initial_default;
13130     titlescreen[i] = titlescreen_default;
13131     titlemessage_initial[i] = titlemessage_initial_default;
13132     titlemessage[i] = titlemessage_default;
13133   }
13134
13135   // special case: initialize "ARG_DEFAULT" values in static default config
13136   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13137   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13138   {
13139     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13140       continue;
13141
13142     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13143     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13144     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13145   }
13146
13147   // special case: initialize "ARG_DEFAULT" values in static default config
13148   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13149   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13150   {
13151     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13152     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13153     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13154
13155     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13156       continue;
13157
13158     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13159   }
13160 }
13161
13162 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13163 {
13164   static struct
13165   {
13166     struct XY *dst, *src;
13167   }
13168   game_buttons_xy[] =
13169   {
13170     { &game.button.save,        &game.button.stop       },
13171     { &game.button.pause2,      &game.button.pause      },
13172     { &game.button.load,        &game.button.play       },
13173     { &game.button.undo,        &game.button.stop       },
13174     { &game.button.redo,        &game.button.play       },
13175
13176     { NULL,                     NULL                    }
13177   };
13178   int i, j;
13179
13180   // special case: initialize later added SETUP list size from LEVELS value
13181   if (menu.list_size[GAME_MODE_SETUP] == -1)
13182     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13183
13184   // set default position for snapshot buttons to stop/pause/play buttons
13185   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13186     if ((*game_buttons_xy[i].dst).x == -1 &&
13187         (*game_buttons_xy[i].dst).y == -1)
13188       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13189
13190   // --------------------------------------------------------------------------
13191   // dynamic viewports (including playfield margins, borders and alignments)
13192   // --------------------------------------------------------------------------
13193
13194   // dynamic viewports currently only supported for landscape mode
13195   int display_width  = MAX(video.display_width, video.display_height);
13196   int display_height = MIN(video.display_width, video.display_height);
13197
13198   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13199   {
13200     struct RectWithBorder *vp_window    = &viewport.window[i];
13201     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13202     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13203     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13204     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13205     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13206     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13207     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13208
13209     // adjust window size if min/max width/height is specified
13210
13211     if (vp_window->min_width != -1)
13212     {
13213       int window_width = display_width;
13214
13215       // when using static window height, use aspect ratio of display
13216       if (vp_window->min_height == -1)
13217         window_width = vp_window->height * display_width / display_height;
13218
13219       vp_window->width = MAX(vp_window->min_width, window_width);
13220     }
13221
13222     if (vp_window->min_height != -1)
13223     {
13224       int window_height = display_height;
13225
13226       // when using static window width, use aspect ratio of display
13227       if (vp_window->min_width == -1)
13228         window_height = vp_window->width * display_height / display_width;
13229
13230       vp_window->height = MAX(vp_window->min_height, window_height);
13231     }
13232
13233     if (vp_window->max_width != -1)
13234       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13235
13236     if (vp_window->max_height != -1)
13237       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13238
13239     int playfield_width  = vp_window->width;
13240     int playfield_height = vp_window->height;
13241
13242     // adjust playfield size and position according to specified margins
13243
13244     playfield_width  -= vp_playfield->margin_left;
13245     playfield_width  -= vp_playfield->margin_right;
13246
13247     playfield_height -= vp_playfield->margin_top;
13248     playfield_height -= vp_playfield->margin_bottom;
13249
13250     // adjust playfield size if min/max width/height is specified
13251
13252     if (vp_playfield->min_width != -1)
13253       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13254
13255     if (vp_playfield->min_height != -1)
13256       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13257
13258     if (vp_playfield->max_width != -1)
13259       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13260
13261     if (vp_playfield->max_height != -1)
13262       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13263
13264     // adjust playfield position according to specified alignment
13265
13266     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13267       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13268     else if (vp_playfield->align == ALIGN_CENTER)
13269       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13270     else if (vp_playfield->align == ALIGN_RIGHT)
13271       vp_playfield->x += playfield_width - vp_playfield->width;
13272
13273     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13274       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13275     else if (vp_playfield->valign == VALIGN_MIDDLE)
13276       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13277     else if (vp_playfield->valign == VALIGN_BOTTOM)
13278       vp_playfield->y += playfield_height - vp_playfield->height;
13279
13280     vp_playfield->x += vp_playfield->margin_left;
13281     vp_playfield->y += vp_playfield->margin_top;
13282
13283     // adjust individual playfield borders if only default border is specified
13284
13285     if (vp_playfield->border_left == -1)
13286       vp_playfield->border_left = vp_playfield->border_size;
13287     if (vp_playfield->border_right == -1)
13288       vp_playfield->border_right = vp_playfield->border_size;
13289     if (vp_playfield->border_top == -1)
13290       vp_playfield->border_top = vp_playfield->border_size;
13291     if (vp_playfield->border_bottom == -1)
13292       vp_playfield->border_bottom = vp_playfield->border_size;
13293
13294     // set dynamic playfield borders if borders are specified as undefined
13295     // (but only if window size was dynamic and playfield size was static)
13296
13297     if (dynamic_window_width && !dynamic_playfield_width)
13298     {
13299       if (vp_playfield->border_left == -1)
13300       {
13301         vp_playfield->border_left = (vp_playfield->x -
13302                                      vp_playfield->margin_left);
13303         vp_playfield->x     -= vp_playfield->border_left;
13304         vp_playfield->width += vp_playfield->border_left;
13305       }
13306
13307       if (vp_playfield->border_right == -1)
13308       {
13309         vp_playfield->border_right = (vp_window->width -
13310                                       vp_playfield->x -
13311                                       vp_playfield->width -
13312                                       vp_playfield->margin_right);
13313         vp_playfield->width += vp_playfield->border_right;
13314       }
13315     }
13316
13317     if (dynamic_window_height && !dynamic_playfield_height)
13318     {
13319       if (vp_playfield->border_top == -1)
13320       {
13321         vp_playfield->border_top = (vp_playfield->y -
13322                                     vp_playfield->margin_top);
13323         vp_playfield->y      -= vp_playfield->border_top;
13324         vp_playfield->height += vp_playfield->border_top;
13325       }
13326
13327       if (vp_playfield->border_bottom == -1)
13328       {
13329         vp_playfield->border_bottom = (vp_window->height -
13330                                        vp_playfield->y -
13331                                        vp_playfield->height -
13332                                        vp_playfield->margin_bottom);
13333         vp_playfield->height += vp_playfield->border_bottom;
13334       }
13335     }
13336
13337     // adjust playfield size to be a multiple of a defined alignment tile size
13338
13339     int align_size = vp_playfield->align_size;
13340     int playfield_xtiles = vp_playfield->width  / align_size;
13341     int playfield_ytiles = vp_playfield->height / align_size;
13342     int playfield_width_corrected  = playfield_xtiles * align_size;
13343     int playfield_height_corrected = playfield_ytiles * align_size;
13344     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13345                                  i == GFX_SPECIAL_ARG_EDITOR);
13346
13347     if (is_playfield_mode &&
13348         dynamic_playfield_width &&
13349         vp_playfield->width != playfield_width_corrected)
13350     {
13351       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13352
13353       vp_playfield->width = playfield_width_corrected;
13354
13355       if (vp_playfield->align == ALIGN_LEFT)
13356       {
13357         vp_playfield->border_left += playfield_xdiff;
13358       }
13359       else if (vp_playfield->align == ALIGN_RIGHT)
13360       {
13361         vp_playfield->border_right += playfield_xdiff;
13362       }
13363       else if (vp_playfield->align == ALIGN_CENTER)
13364       {
13365         int border_left_diff  = playfield_xdiff / 2;
13366         int border_right_diff = playfield_xdiff - border_left_diff;
13367
13368         vp_playfield->border_left  += border_left_diff;
13369         vp_playfield->border_right += border_right_diff;
13370       }
13371     }
13372
13373     if (is_playfield_mode &&
13374         dynamic_playfield_height &&
13375         vp_playfield->height != playfield_height_corrected)
13376     {
13377       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13378
13379       vp_playfield->height = playfield_height_corrected;
13380
13381       if (vp_playfield->valign == VALIGN_TOP)
13382       {
13383         vp_playfield->border_top += playfield_ydiff;
13384       }
13385       else if (vp_playfield->align == VALIGN_BOTTOM)
13386       {
13387         vp_playfield->border_right += playfield_ydiff;
13388       }
13389       else if (vp_playfield->align == VALIGN_MIDDLE)
13390       {
13391         int border_top_diff    = playfield_ydiff / 2;
13392         int border_bottom_diff = playfield_ydiff - border_top_diff;
13393
13394         vp_playfield->border_top    += border_top_diff;
13395         vp_playfield->border_bottom += border_bottom_diff;
13396       }
13397     }
13398
13399     // adjust door positions according to specified alignment
13400
13401     for (j = 0; j < 2; j++)
13402     {
13403       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13404
13405       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13406         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13407       else if (vp_door->align == ALIGN_CENTER)
13408         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13409       else if (vp_door->align == ALIGN_RIGHT)
13410         vp_door->x += vp_window->width - vp_door->width;
13411
13412       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13413         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13414       else if (vp_door->valign == VALIGN_MIDDLE)
13415         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13416       else if (vp_door->valign == VALIGN_BOTTOM)
13417         vp_door->y += vp_window->height - vp_door->height;
13418     }
13419   }
13420 }
13421
13422 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13423 {
13424   static struct
13425   {
13426     struct XYTileSize *dst, *src;
13427     int graphic;
13428   }
13429   editor_buttons_xy[] =
13430   {
13431     {
13432       &editor.button.element_left,      &editor.palette.element_left,
13433       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13434     },
13435     {
13436       &editor.button.element_middle,    &editor.palette.element_middle,
13437       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13438     },
13439     {
13440       &editor.button.element_right,     &editor.palette.element_right,
13441       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13442     },
13443
13444     { NULL,                     NULL                    }
13445   };
13446   int i;
13447
13448   // set default position for element buttons to element graphics
13449   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13450   {
13451     if ((*editor_buttons_xy[i].dst).x == -1 &&
13452         (*editor_buttons_xy[i].dst).y == -1)
13453     {
13454       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13455
13456       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13457
13458       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13459     }
13460   }
13461
13462   // adjust editor palette rows and columns if specified to be dynamic
13463
13464   if (editor.palette.cols == -1)
13465   {
13466     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13467     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13468     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13469
13470     editor.palette.cols = (vp_width - sc_width) / bt_width;
13471
13472     if (editor.palette.x == -1)
13473     {
13474       int palette_width = editor.palette.cols * bt_width + sc_width;
13475
13476       editor.palette.x = (vp_width - palette_width) / 2;
13477     }
13478   }
13479
13480   if (editor.palette.rows == -1)
13481   {
13482     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13483     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13484     int tx_height = getFontHeight(FONT_TEXT_2);
13485
13486     editor.palette.rows = (vp_height - tx_height) / bt_height;
13487
13488     if (editor.palette.y == -1)
13489     {
13490       int palette_height = editor.palette.rows * bt_height + tx_height;
13491
13492       editor.palette.y = (vp_height - palette_height) / 2;
13493     }
13494   }
13495 }
13496
13497 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13498                                                       boolean initialize)
13499 {
13500   // special case: check if network and preview player positions are redefined,
13501   // to compare this later against the main menu level preview being redefined
13502   struct TokenIntPtrInfo menu_config_players[] =
13503   {
13504     { "main.network_players.x", &menu.main.network_players.redefined    },
13505     { "main.network_players.y", &menu.main.network_players.redefined    },
13506     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13507     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13508     { "preview.x",              &preview.redefined                      },
13509     { "preview.y",              &preview.redefined                      }
13510   };
13511   int i;
13512
13513   if (initialize)
13514   {
13515     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13516       *menu_config_players[i].value = FALSE;
13517   }
13518   else
13519   {
13520     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13521       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13522         *menu_config_players[i].value = TRUE;
13523   }
13524 }
13525
13526 static void InitMenuDesignSettings_PreviewPlayers(void)
13527 {
13528   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13529 }
13530
13531 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13532 {
13533   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13534 }
13535
13536 static void LoadMenuDesignSettingsFromFilename(char *filename)
13537 {
13538   static struct TitleFadingInfo tfi;
13539   static struct TitleMessageInfo tmi;
13540   static struct TokenInfo title_tokens[] =
13541   {
13542     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13543     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13544     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13545     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13546     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13547
13548     { -1,               NULL,                   NULL                    }
13549   };
13550   static struct TokenInfo titlemessage_tokens[] =
13551   {
13552     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13553     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13554     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13555     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13556     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13557     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13558     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13559     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13560     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13561     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13562     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13563     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13564     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13565     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13566     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13567     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13568     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13569     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13570
13571     { -1,               NULL,                   NULL                    }
13572   };
13573   static struct
13574   {
13575     struct TitleFadingInfo *info;
13576     char *text;
13577   }
13578   title_info[] =
13579   {
13580     // initialize first titles from "enter screen" definitions, if defined
13581     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13582     { &title_first_default,             "menu.enter_screen.TITLE"       },
13583
13584     // initialize title screens from "next screen" definitions, if defined
13585     { &title_initial_default,           "menu.next_screen.TITLE"        },
13586     { &title_default,                   "menu.next_screen.TITLE"        },
13587
13588     { NULL,                             NULL                            }
13589   };
13590   static struct
13591   {
13592     struct TitleMessageInfo *array;
13593     char *text;
13594   }
13595   titlemessage_arrays[] =
13596   {
13597     // initialize first titles from "enter screen" definitions, if defined
13598     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13599     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13600     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13601     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13602
13603     // initialize titles from "next screen" definitions, if defined
13604     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13605     { titlescreen,                      "menu.next_screen.TITLE"        },
13606     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13607     { titlemessage,                     "menu.next_screen.TITLE"        },
13608
13609     // overwrite titles with title definitions, if defined
13610     { titlescreen_initial_first,        "[title_initial]"               },
13611     { titlescreen_first,                "[title]"                       },
13612     { titlemessage_initial_first,       "[title_initial]"               },
13613     { titlemessage_first,               "[title]"                       },
13614
13615     { titlescreen_initial,              "[title_initial]"               },
13616     { titlescreen,                      "[title]"                       },
13617     { titlemessage_initial,             "[title_initial]"               },
13618     { titlemessage,                     "[title]"                       },
13619
13620     // overwrite titles with title screen/message definitions, if defined
13621     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13622     { titlescreen_first,                "[titlescreen]"                 },
13623     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13624     { titlemessage_first,               "[titlemessage]"                },
13625
13626     { titlescreen_initial,              "[titlescreen_initial]"         },
13627     { titlescreen,                      "[titlescreen]"                 },
13628     { titlemessage_initial,             "[titlemessage_initial]"        },
13629     { titlemessage,                     "[titlemessage]"                },
13630
13631     { NULL,                             NULL                            }
13632   };
13633   SetupFileHash *setup_file_hash;
13634   int i, j, k;
13635
13636   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13637     return;
13638
13639   // the following initializes hierarchical values from dynamic configuration
13640
13641   // special case: initialize with default values that may be overwritten
13642   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13643   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13644   {
13645     struct TokenIntPtrInfo menu_config[] =
13646     {
13647       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13648       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13649       { "menu.list_size",       &menu.list_size[i]      }
13650     };
13651
13652     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13653     {
13654       char *token = menu_config[j].token;
13655       char *value = getHashEntry(setup_file_hash, token);
13656
13657       if (value != NULL)
13658         *menu_config[j].value = get_integer_from_string(value);
13659     }
13660   }
13661
13662   // special case: initialize with default values that may be overwritten
13663   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13664   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13665   {
13666     struct TokenIntPtrInfo menu_config[] =
13667     {
13668       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13669       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13670       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13671       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13672       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13673     };
13674
13675     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13676     {
13677       char *token = menu_config[j].token;
13678       char *value = getHashEntry(setup_file_hash, token);
13679
13680       if (value != NULL)
13681         *menu_config[j].value = get_integer_from_string(value);
13682     }
13683   }
13684
13685   // special case: initialize with default values that may be overwritten
13686   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13687   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13688   {
13689     struct TokenIntPtrInfo menu_config[] =
13690     {
13691       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13692       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13693     };
13694
13695     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13696     {
13697       char *token = menu_config[j].token;
13698       char *value = getHashEntry(setup_file_hash, token);
13699
13700       if (value != NULL)
13701         *menu_config[j].value = get_integer_from_string(value);
13702     }
13703   }
13704
13705   // special case: initialize with default values that may be overwritten
13706   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13707   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13708   {
13709     struct TokenIntPtrInfo menu_config[] =
13710     {
13711       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13712       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13713       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13714       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13715       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13716       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13717       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13718       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13719       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13720       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13721     };
13722
13723     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13724     {
13725       char *token = menu_config[j].token;
13726       char *value = getHashEntry(setup_file_hash, token);
13727
13728       if (value != NULL)
13729         *menu_config[j].value = get_integer_from_string(value);
13730     }
13731   }
13732
13733   // special case: initialize with default values that may be overwritten
13734   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13735   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13736   {
13737     struct TokenIntPtrInfo menu_config[] =
13738     {
13739       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13740       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13741       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13742       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13743       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13744       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13745       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13746       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13747       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13748     };
13749
13750     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13751     {
13752       char *token = menu_config[j].token;
13753       char *value = getHashEntry(setup_file_hash, token);
13754
13755       if (value != NULL)
13756         *menu_config[j].value = get_token_parameter_value(token, value);
13757     }
13758   }
13759
13760   // special case: initialize with default values that may be overwritten
13761   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13762   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13763   {
13764     struct
13765     {
13766       char *token_prefix;
13767       struct RectWithBorder *struct_ptr;
13768     }
13769     vp_struct[] =
13770     {
13771       { "viewport.window",      &viewport.window[i]     },
13772       { "viewport.playfield",   &viewport.playfield[i]  },
13773       { "viewport.door_1",      &viewport.door_1[i]     },
13774       { "viewport.door_2",      &viewport.door_2[i]     }
13775     };
13776
13777     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13778     {
13779       struct TokenIntPtrInfo vp_config[] =
13780       {
13781         { ".x",                 &vp_struct[j].struct_ptr->x             },
13782         { ".y",                 &vp_struct[j].struct_ptr->y             },
13783         { ".width",             &vp_struct[j].struct_ptr->width         },
13784         { ".height",            &vp_struct[j].struct_ptr->height        },
13785         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13786         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13787         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13788         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13789         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13790         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13791         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13792         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13793         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13794         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13795         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13796         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13797         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13798         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13799         { ".align",             &vp_struct[j].struct_ptr->align         },
13800         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13801       };
13802
13803       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13804       {
13805         char *token = getStringCat2(vp_struct[j].token_prefix,
13806                                     vp_config[k].token);
13807         char *value = getHashEntry(setup_file_hash, token);
13808
13809         if (value != NULL)
13810           *vp_config[k].value = get_token_parameter_value(token, value);
13811
13812         free(token);
13813       }
13814     }
13815   }
13816
13817   // special case: initialize with default values that may be overwritten
13818   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13819   for (i = 0; title_info[i].info != NULL; i++)
13820   {
13821     struct TitleFadingInfo *info = title_info[i].info;
13822     char *base_token = title_info[i].text;
13823
13824     for (j = 0; title_tokens[j].type != -1; j++)
13825     {
13826       char *token = getStringCat2(base_token, title_tokens[j].text);
13827       char *value = getHashEntry(setup_file_hash, token);
13828
13829       if (value != NULL)
13830       {
13831         int parameter_value = get_token_parameter_value(token, value);
13832
13833         tfi = *info;
13834
13835         *(int *)title_tokens[j].value = (int)parameter_value;
13836
13837         *info = tfi;
13838       }
13839
13840       free(token);
13841     }
13842   }
13843
13844   // special case: initialize with default values that may be overwritten
13845   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13846   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13847   {
13848     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13849     char *base_token = titlemessage_arrays[i].text;
13850
13851     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13852     {
13853       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13854       char *value = getHashEntry(setup_file_hash, token);
13855
13856       if (value != NULL)
13857       {
13858         int parameter_value = get_token_parameter_value(token, value);
13859
13860         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13861         {
13862           tmi = array[k];
13863
13864           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13865             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13866           else
13867             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13868
13869           array[k] = tmi;
13870         }
13871       }
13872
13873       free(token);
13874     }
13875   }
13876
13877   // read (and overwrite with) values that may be specified in config file
13878   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13879
13880   // special case: check if network and preview player positions are redefined
13881   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13882
13883   freeSetupFileHash(setup_file_hash);
13884 }
13885
13886 void LoadMenuDesignSettings(void)
13887 {
13888   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13889
13890   InitMenuDesignSettings_Static();
13891   InitMenuDesignSettings_SpecialPreProcessing();
13892   InitMenuDesignSettings_PreviewPlayers();
13893
13894   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13895   {
13896     // first look for special settings configured in level series config
13897     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13898
13899     if (fileExists(filename_base))
13900       LoadMenuDesignSettingsFromFilename(filename_base);
13901   }
13902
13903   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13904
13905   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13906     LoadMenuDesignSettingsFromFilename(filename_local);
13907
13908   InitMenuDesignSettings_SpecialPostProcessing();
13909 }
13910
13911 void LoadMenuDesignSettings_AfterGraphics(void)
13912 {
13913   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13914 }
13915
13916 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13917                                 boolean ignore_defaults)
13918 {
13919   int i;
13920
13921   for (i = 0; sound_config_vars[i].token != NULL; i++)
13922   {
13923     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13924
13925     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13926     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13927       continue;
13928
13929     if (value != NULL)
13930       *sound_config_vars[i].value =
13931         get_token_parameter_value(sound_config_vars[i].token, value);
13932   }
13933 }
13934
13935 void InitSoundSettings_Static(void)
13936 {
13937   // always start with reliable default values from static default config
13938   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13939 }
13940
13941 static void LoadSoundSettingsFromFilename(char *filename)
13942 {
13943   SetupFileHash *setup_file_hash;
13944
13945   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13946     return;
13947
13948   // read (and overwrite with) values that may be specified in config file
13949   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13950
13951   freeSetupFileHash(setup_file_hash);
13952 }
13953
13954 void LoadSoundSettings(void)
13955 {
13956   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13957
13958   InitSoundSettings_Static();
13959
13960   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13961   {
13962     // first look for special settings configured in level series config
13963     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13964
13965     if (fileExists(filename_base))
13966       LoadSoundSettingsFromFilename(filename_base);
13967   }
13968
13969   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13970
13971   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13972     LoadSoundSettingsFromFilename(filename_local);
13973 }
13974
13975 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13976 {
13977   char *filename = getEditorSetupFilename();
13978   SetupFileList *setup_file_list, *list;
13979   SetupFileHash *element_hash;
13980   int num_unknown_tokens = 0;
13981   int i;
13982
13983   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13984     return;
13985
13986   element_hash = newSetupFileHash();
13987
13988   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13989     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13990
13991   // determined size may be larger than needed (due to unknown elements)
13992   *num_elements = 0;
13993   for (list = setup_file_list; list != NULL; list = list->next)
13994     (*num_elements)++;
13995
13996   // add space for up to 3 more elements for padding that may be needed
13997   *num_elements += 3;
13998
13999   // free memory for old list of elements, if needed
14000   checked_free(*elements);
14001
14002   // allocate memory for new list of elements
14003   *elements = checked_malloc(*num_elements * sizeof(int));
14004
14005   *num_elements = 0;
14006   for (list = setup_file_list; list != NULL; list = list->next)
14007   {
14008     char *value = getHashEntry(element_hash, list->token);
14009
14010     if (value == NULL)          // try to find obsolete token mapping
14011     {
14012       char *mapped_token = get_mapped_token(list->token);
14013
14014       if (mapped_token != NULL)
14015       {
14016         value = getHashEntry(element_hash, mapped_token);
14017
14018         free(mapped_token);
14019       }
14020     }
14021
14022     if (value != NULL)
14023     {
14024       (*elements)[(*num_elements)++] = atoi(value);
14025     }
14026     else
14027     {
14028       if (num_unknown_tokens == 0)
14029       {
14030         Warn("---");
14031         Warn("unknown token(s) found in config file:");
14032         Warn("- config file: '%s'", filename);
14033
14034         num_unknown_tokens++;
14035       }
14036
14037       Warn("- token: '%s'", list->token);
14038     }
14039   }
14040
14041   if (num_unknown_tokens > 0)
14042     Warn("---");
14043
14044   while (*num_elements % 4)     // pad with empty elements, if needed
14045     (*elements)[(*num_elements)++] = EL_EMPTY;
14046
14047   freeSetupFileList(setup_file_list);
14048   freeSetupFileHash(element_hash);
14049
14050 #if 0
14051   for (i = 0; i < *num_elements; i++)
14052     Debug("editor", "element '%s' [%d]\n",
14053           element_info[(*elements)[i]].token_name, (*elements)[i]);
14054 #endif
14055 }
14056
14057 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14058                                                      boolean is_sound)
14059 {
14060   SetupFileHash *setup_file_hash = NULL;
14061   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14062   char *filename_music, *filename_prefix, *filename_info;
14063   struct
14064   {
14065     char *token;
14066     char **value_ptr;
14067   }
14068   token_to_value_ptr[] =
14069   {
14070     { "title_header",   &tmp_music_file_info.title_header       },
14071     { "artist_header",  &tmp_music_file_info.artist_header      },
14072     { "album_header",   &tmp_music_file_info.album_header       },
14073     { "year_header",    &tmp_music_file_info.year_header        },
14074     { "played_header",  &tmp_music_file_info.played_header      },
14075
14076     { "title",          &tmp_music_file_info.title              },
14077     { "artist",         &tmp_music_file_info.artist             },
14078     { "album",          &tmp_music_file_info.album              },
14079     { "year",           &tmp_music_file_info.year               },
14080     { "played",         &tmp_music_file_info.played             },
14081
14082     { NULL,             NULL                                    },
14083   };
14084   int i;
14085
14086   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14087                     getCustomMusicFilename(basename));
14088
14089   if (filename_music == NULL)
14090     return NULL;
14091
14092   // ---------- try to replace file extension ----------
14093
14094   filename_prefix = getStringCopy(filename_music);
14095   if (strrchr(filename_prefix, '.') != NULL)
14096     *strrchr(filename_prefix, '.') = '\0';
14097   filename_info = getStringCat2(filename_prefix, ".txt");
14098
14099   if (fileExists(filename_info))
14100     setup_file_hash = loadSetupFileHash(filename_info);
14101
14102   free(filename_prefix);
14103   free(filename_info);
14104
14105   if (setup_file_hash == NULL)
14106   {
14107     // ---------- try to add file extension ----------
14108
14109     filename_prefix = getStringCopy(filename_music);
14110     filename_info = getStringCat2(filename_prefix, ".txt");
14111
14112     if (fileExists(filename_info))
14113       setup_file_hash = loadSetupFileHash(filename_info);
14114
14115     free(filename_prefix);
14116     free(filename_info);
14117   }
14118
14119   if (setup_file_hash == NULL)
14120     return NULL;
14121
14122   // ---------- music file info found ----------
14123
14124   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14125
14126   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14127   {
14128     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14129
14130     *token_to_value_ptr[i].value_ptr =
14131       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14132   }
14133
14134   tmp_music_file_info.basename = getStringCopy(basename);
14135   tmp_music_file_info.music = music;
14136   tmp_music_file_info.is_sound = is_sound;
14137
14138   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14139   *new_music_file_info = tmp_music_file_info;
14140
14141   return new_music_file_info;
14142 }
14143
14144 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14145 {
14146   return get_music_file_info_ext(basename, music, FALSE);
14147 }
14148
14149 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14150 {
14151   return get_music_file_info_ext(basename, sound, TRUE);
14152 }
14153
14154 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14155                                      char *basename, boolean is_sound)
14156 {
14157   for (; list != NULL; list = list->next)
14158     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14159       return TRUE;
14160
14161   return FALSE;
14162 }
14163
14164 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14165 {
14166   return music_info_listed_ext(list, basename, FALSE);
14167 }
14168
14169 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14170 {
14171   return music_info_listed_ext(list, basename, TRUE);
14172 }
14173
14174 void LoadMusicInfo(void)
14175 {
14176   int num_music_noconf = getMusicListSize_NoConf();
14177   int num_music = getMusicListSize();
14178   int num_sounds = getSoundListSize();
14179   struct FileInfo *music, *sound;
14180   struct MusicFileInfo *next, **new;
14181
14182   int i;
14183
14184   while (music_file_info != NULL)
14185   {
14186     next = music_file_info->next;
14187
14188     checked_free(music_file_info->basename);
14189
14190     checked_free(music_file_info->title_header);
14191     checked_free(music_file_info->artist_header);
14192     checked_free(music_file_info->album_header);
14193     checked_free(music_file_info->year_header);
14194     checked_free(music_file_info->played_header);
14195
14196     checked_free(music_file_info->title);
14197     checked_free(music_file_info->artist);
14198     checked_free(music_file_info->album);
14199     checked_free(music_file_info->year);
14200     checked_free(music_file_info->played);
14201
14202     free(music_file_info);
14203
14204     music_file_info = next;
14205   }
14206
14207   new = &music_file_info;
14208
14209   // get (configured or unconfigured) music file info for all levels
14210   for (i = leveldir_current->first_level;
14211        i <= leveldir_current->last_level; i++)
14212   {
14213     int music_nr;
14214
14215     if (levelset.music[i] != MUS_UNDEFINED)
14216     {
14217       // get music file info for configured level music
14218       music_nr = levelset.music[i];
14219     }
14220     else if (num_music_noconf > 0)
14221     {
14222       // get music file info for unconfigured level music
14223       int level_pos = i - leveldir_current->first_level;
14224
14225       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14226     }
14227     else
14228     {
14229       continue;
14230     }
14231
14232     char *basename = getMusicInfoEntryFilename(music_nr);
14233
14234     if (basename == NULL)
14235       continue;
14236
14237     if (!music_info_listed(music_file_info, basename))
14238     {
14239       *new = get_music_file_info(basename, music_nr);
14240
14241       if (*new != NULL)
14242         new = &(*new)->next;
14243     }
14244   }
14245
14246   // get music file info for all remaining configured music files
14247   for (i = 0; i < num_music; i++)
14248   {
14249     music = getMusicListEntry(i);
14250
14251     if (music->filename == NULL)
14252       continue;
14253
14254     if (strEqual(music->filename, UNDEFINED_FILENAME))
14255       continue;
14256
14257     // a configured file may be not recognized as music
14258     if (!FileIsMusic(music->filename))
14259       continue;
14260
14261     if (!music_info_listed(music_file_info, music->filename))
14262     {
14263       *new = get_music_file_info(music->filename, i);
14264
14265       if (*new != NULL)
14266         new = &(*new)->next;
14267     }
14268   }
14269
14270   // get sound file info for all configured sound files
14271   for (i = 0; i < num_sounds; i++)
14272   {
14273     sound = getSoundListEntry(i);
14274
14275     if (sound->filename == NULL)
14276       continue;
14277
14278     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14279       continue;
14280
14281     // a configured file may be not recognized as sound
14282     if (!FileIsSound(sound->filename))
14283       continue;
14284
14285     if (!sound_info_listed(music_file_info, sound->filename))
14286     {
14287       *new = get_sound_file_info(sound->filename, i);
14288       if (*new != NULL)
14289         new = &(*new)->next;
14290     }
14291   }
14292
14293   // add pointers to previous list nodes
14294
14295   struct MusicFileInfo *node = music_file_info;
14296
14297   while (node != NULL)
14298   {
14299     if (node->next)
14300       node->next->prev = node;
14301
14302     node = node->next;
14303   }
14304 }
14305
14306 static void add_helpanim_entry(int element, int action, int direction,
14307                                int delay, int *num_list_entries)
14308 {
14309   struct HelpAnimInfo *new_list_entry;
14310   (*num_list_entries)++;
14311
14312   helpanim_info =
14313     checked_realloc(helpanim_info,
14314                     *num_list_entries * sizeof(struct HelpAnimInfo));
14315   new_list_entry = &helpanim_info[*num_list_entries - 1];
14316
14317   new_list_entry->element = element;
14318   new_list_entry->action = action;
14319   new_list_entry->direction = direction;
14320   new_list_entry->delay = delay;
14321 }
14322
14323 static void print_unknown_token(char *filename, char *token, int token_nr)
14324 {
14325   if (token_nr == 0)
14326   {
14327     Warn("---");
14328     Warn("unknown token(s) found in config file:");
14329     Warn("- config file: '%s'", filename);
14330   }
14331
14332   Warn("- token: '%s'", token);
14333 }
14334
14335 static void print_unknown_token_end(int token_nr)
14336 {
14337   if (token_nr > 0)
14338     Warn("---");
14339 }
14340
14341 void LoadHelpAnimInfo(void)
14342 {
14343   char *filename = getHelpAnimFilename();
14344   SetupFileList *setup_file_list = NULL, *list;
14345   SetupFileHash *element_hash, *action_hash, *direction_hash;
14346   int num_list_entries = 0;
14347   int num_unknown_tokens = 0;
14348   int i;
14349
14350   if (fileExists(filename))
14351     setup_file_list = loadSetupFileList(filename);
14352
14353   if (setup_file_list == NULL)
14354   {
14355     // use reliable default values from static configuration
14356     SetupFileList *insert_ptr;
14357
14358     insert_ptr = setup_file_list =
14359       newSetupFileList(helpanim_config[0].token,
14360                        helpanim_config[0].value);
14361
14362     for (i = 1; helpanim_config[i].token; i++)
14363       insert_ptr = addListEntry(insert_ptr,
14364                                 helpanim_config[i].token,
14365                                 helpanim_config[i].value);
14366   }
14367
14368   element_hash   = newSetupFileHash();
14369   action_hash    = newSetupFileHash();
14370   direction_hash = newSetupFileHash();
14371
14372   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14373     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14374
14375   for (i = 0; i < NUM_ACTIONS; i++)
14376     setHashEntry(action_hash, element_action_info[i].suffix,
14377                  i_to_a(element_action_info[i].value));
14378
14379   // do not store direction index (bit) here, but direction value!
14380   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14381     setHashEntry(direction_hash, element_direction_info[i].suffix,
14382                  i_to_a(1 << element_direction_info[i].value));
14383
14384   for (list = setup_file_list; list != NULL; list = list->next)
14385   {
14386     char *element_token, *action_token, *direction_token;
14387     char *element_value, *action_value, *direction_value;
14388     int delay = atoi(list->value);
14389
14390     if (strEqual(list->token, "end"))
14391     {
14392       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14393
14394       continue;
14395     }
14396
14397     /* first try to break element into element/action/direction parts;
14398        if this does not work, also accept combined "element[.act][.dir]"
14399        elements (like "dynamite.active"), which are unique elements */
14400
14401     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14402     {
14403       element_value = getHashEntry(element_hash, list->token);
14404       if (element_value != NULL)        // element found
14405         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14406                            &num_list_entries);
14407       else
14408       {
14409         // no further suffixes found -- this is not an element
14410         print_unknown_token(filename, list->token, num_unknown_tokens++);
14411       }
14412
14413       continue;
14414     }
14415
14416     // token has format "<prefix>.<something>"
14417
14418     action_token = strchr(list->token, '.');    // suffix may be action ...
14419     direction_token = action_token;             // ... or direction
14420
14421     element_token = getStringCopy(list->token);
14422     *strchr(element_token, '.') = '\0';
14423
14424     element_value = getHashEntry(element_hash, element_token);
14425
14426     if (element_value == NULL)          // this is no element
14427     {
14428       element_value = getHashEntry(element_hash, list->token);
14429       if (element_value != NULL)        // combined element found
14430         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14431                            &num_list_entries);
14432       else
14433         print_unknown_token(filename, list->token, num_unknown_tokens++);
14434
14435       free(element_token);
14436
14437       continue;
14438     }
14439
14440     action_value = getHashEntry(action_hash, action_token);
14441
14442     if (action_value != NULL)           // action found
14443     {
14444       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14445                     &num_list_entries);
14446
14447       free(element_token);
14448
14449       continue;
14450     }
14451
14452     direction_value = getHashEntry(direction_hash, direction_token);
14453
14454     if (direction_value != NULL)        // direction found
14455     {
14456       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14457                          &num_list_entries);
14458
14459       free(element_token);
14460
14461       continue;
14462     }
14463
14464     if (strchr(action_token + 1, '.') == NULL)
14465     {
14466       // no further suffixes found -- this is not an action nor direction
14467
14468       element_value = getHashEntry(element_hash, list->token);
14469       if (element_value != NULL)        // combined element found
14470         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14471                            &num_list_entries);
14472       else
14473         print_unknown_token(filename, list->token, num_unknown_tokens++);
14474
14475       free(element_token);
14476
14477       continue;
14478     }
14479
14480     // token has format "<prefix>.<suffix>.<something>"
14481
14482     direction_token = strchr(action_token + 1, '.');
14483
14484     action_token = getStringCopy(action_token);
14485     *strchr(action_token + 1, '.') = '\0';
14486
14487     action_value = getHashEntry(action_hash, action_token);
14488
14489     if (action_value == NULL)           // this is no action
14490     {
14491       element_value = getHashEntry(element_hash, list->token);
14492       if (element_value != NULL)        // combined element found
14493         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14494                            &num_list_entries);
14495       else
14496         print_unknown_token(filename, list->token, num_unknown_tokens++);
14497
14498       free(element_token);
14499       free(action_token);
14500
14501       continue;
14502     }
14503
14504     direction_value = getHashEntry(direction_hash, direction_token);
14505
14506     if (direction_value != NULL)        // direction found
14507     {
14508       add_helpanim_entry(atoi(element_value), atoi(action_value),
14509                          atoi(direction_value), delay, &num_list_entries);
14510
14511       free(element_token);
14512       free(action_token);
14513
14514       continue;
14515     }
14516
14517     // this is no direction
14518
14519     element_value = getHashEntry(element_hash, list->token);
14520     if (element_value != NULL)          // combined element found
14521       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14522                          &num_list_entries);
14523     else
14524       print_unknown_token(filename, list->token, num_unknown_tokens++);
14525
14526     free(element_token);
14527     free(action_token);
14528   }
14529
14530   print_unknown_token_end(num_unknown_tokens);
14531
14532   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14533   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14534
14535   freeSetupFileList(setup_file_list);
14536   freeSetupFileHash(element_hash);
14537   freeSetupFileHash(action_hash);
14538   freeSetupFileHash(direction_hash);
14539
14540 #if 0
14541   for (i = 0; i < num_list_entries; i++)
14542     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14543           EL_NAME(helpanim_info[i].element),
14544           helpanim_info[i].element,
14545           helpanim_info[i].action,
14546           helpanim_info[i].direction,
14547           helpanim_info[i].delay);
14548 #endif
14549 }
14550
14551 void LoadHelpTextInfo(void)
14552 {
14553   char *filename = getHelpTextFilename();
14554   int i;
14555
14556   if (helptext_info != NULL)
14557   {
14558     freeSetupFileHash(helptext_info);
14559     helptext_info = NULL;
14560   }
14561
14562   if (fileExists(filename))
14563     helptext_info = loadSetupFileHash(filename);
14564
14565   if (helptext_info == NULL)
14566   {
14567     // use reliable default values from static configuration
14568     helptext_info = newSetupFileHash();
14569
14570     for (i = 0; helptext_config[i].token; i++)
14571       setHashEntry(helptext_info,
14572                    helptext_config[i].token,
14573                    helptext_config[i].value);
14574   }
14575
14576 #if 0
14577   BEGIN_HASH_ITERATION(helptext_info, itr)
14578   {
14579     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14580           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14581   }
14582   END_HASH_ITERATION(hash, itr)
14583 #endif
14584 }
14585
14586
14587 // ----------------------------------------------------------------------------
14588 // convert levels
14589 // ----------------------------------------------------------------------------
14590
14591 #define MAX_NUM_CONVERT_LEVELS          1000
14592
14593 void ConvertLevels(void)
14594 {
14595   static LevelDirTree *convert_leveldir = NULL;
14596   static int convert_level_nr = -1;
14597   static int num_levels_handled = 0;
14598   static int num_levels_converted = 0;
14599   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14600   int i;
14601
14602   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14603                                                global.convert_leveldir);
14604
14605   if (convert_leveldir == NULL)
14606     Fail("no such level identifier: '%s'", global.convert_leveldir);
14607
14608   leveldir_current = convert_leveldir;
14609
14610   if (global.convert_level_nr != -1)
14611   {
14612     convert_leveldir->first_level = global.convert_level_nr;
14613     convert_leveldir->last_level  = global.convert_level_nr;
14614   }
14615
14616   convert_level_nr = convert_leveldir->first_level;
14617
14618   PrintLine("=", 79);
14619   Print("Converting levels\n");
14620   PrintLine("-", 79);
14621   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14622   Print("Level series name:       '%s'\n", convert_leveldir->name);
14623   Print("Level series author:     '%s'\n", convert_leveldir->author);
14624   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14625   PrintLine("=", 79);
14626   Print("\n");
14627
14628   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14629     levels_failed[i] = FALSE;
14630
14631   while (convert_level_nr <= convert_leveldir->last_level)
14632   {
14633     char *level_filename;
14634     boolean new_level;
14635
14636     level_nr = convert_level_nr++;
14637
14638     Print("Level %03d: ", level_nr);
14639
14640     LoadLevel(level_nr);
14641     if (level.no_level_file || level.no_valid_file)
14642     {
14643       Print("(no level)\n");
14644       continue;
14645     }
14646
14647     Print("converting level ... ");
14648
14649 #if 0
14650     // special case: conversion of some EMC levels as requested by ACME
14651     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14652 #endif
14653
14654     level_filename = getDefaultLevelFilename(level_nr);
14655     new_level = !fileExists(level_filename);
14656
14657     if (new_level)
14658     {
14659       SaveLevel(level_nr);
14660
14661       num_levels_converted++;
14662
14663       Print("converted.\n");
14664     }
14665     else
14666     {
14667       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14668         levels_failed[level_nr] = TRUE;
14669
14670       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14671     }
14672
14673     num_levels_handled++;
14674   }
14675
14676   Print("\n");
14677   PrintLine("=", 79);
14678   Print("Number of levels handled: %d\n", num_levels_handled);
14679   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14680          (num_levels_handled ?
14681           num_levels_converted * 100 / num_levels_handled : 0));
14682   PrintLine("-", 79);
14683   Print("Summary (for automatic parsing by scripts):\n");
14684   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14685          convert_leveldir->identifier, num_levels_converted,
14686          num_levels_handled,
14687          (num_levels_handled ?
14688           num_levels_converted * 100 / num_levels_handled : 0));
14689
14690   if (num_levels_handled != num_levels_converted)
14691   {
14692     Print(", FAILED:");
14693     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14694       if (levels_failed[i])
14695         Print(" %03d", i);
14696   }
14697
14698   Print("\n");
14699   PrintLine("=", 79);
14700
14701   CloseAllAndExit(0);
14702 }
14703
14704
14705 // ----------------------------------------------------------------------------
14706 // create and save images for use in level sketches (raw BMP format)
14707 // ----------------------------------------------------------------------------
14708
14709 void CreateLevelSketchImages(void)
14710 {
14711   Bitmap *bitmap1;
14712   Bitmap *bitmap2;
14713   int i;
14714
14715   InitElementPropertiesGfxElement();
14716
14717   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14718   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14719
14720   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14721   {
14722     int element = getMappedElement(i);
14723     char basename1[16];
14724     char basename2[16];
14725     char *filename1;
14726     char *filename2;
14727
14728     sprintf(basename1, "%04d.bmp", i);
14729     sprintf(basename2, "%04ds.bmp", i);
14730
14731     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14732     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14733
14734     DrawSizedElement(0, 0, element, TILESIZE);
14735     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14736
14737     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14738       Fail("cannot save level sketch image file '%s'", filename1);
14739
14740     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14741     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14742
14743     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14744       Fail("cannot save level sketch image file '%s'", filename2);
14745
14746     free(filename1);
14747     free(filename2);
14748
14749     // create corresponding SQL statements (for normal and small images)
14750     if (i < 1000)
14751     {
14752       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14753       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14754     }
14755
14756     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14757     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14758
14759     // optional: create content for forum level sketch demonstration post
14760     if (options.debug)
14761       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14762   }
14763
14764   FreeBitmap(bitmap1);
14765   FreeBitmap(bitmap2);
14766
14767   if (options.debug)
14768     fprintf(stderr, "\n");
14769
14770   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14771
14772   CloseAllAndExit(0);
14773 }
14774
14775
14776 // ----------------------------------------------------------------------------
14777 // create and save images for element collecting animations (raw BMP format)
14778 // ----------------------------------------------------------------------------
14779
14780 static boolean createCollectImage(int element)
14781 {
14782   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14783 }
14784
14785 void CreateCollectElementImages(void)
14786 {
14787   int i, j;
14788   int num_steps = 8;
14789   int anim_frames = num_steps - 1;
14790   int tile_size = TILESIZE;
14791   int anim_width  = tile_size * anim_frames;
14792   int anim_height = tile_size;
14793   int num_collect_images = 0;
14794   int pos_collect_images = 0;
14795
14796   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14797     if (createCollectImage(i))
14798       num_collect_images++;
14799
14800   Info("Creating %d element collecting animation images ...",
14801        num_collect_images);
14802
14803   int dst_width  = anim_width * 2;
14804   int dst_height = anim_height * num_collect_images / 2;
14805   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14806   char *basename_bmp = "RocksCollect.bmp";
14807   char *basename_png = "RocksCollect.png";
14808   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14809   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14810   int len_filename_bmp = strlen(filename_bmp);
14811   int len_filename_png = strlen(filename_png);
14812   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14813   char cmd_convert[max_command_len];
14814
14815   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14816            filename_bmp,
14817            filename_png);
14818
14819   // force using RGBA surface for destination bitmap
14820   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14821                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14822
14823   dst_bitmap->surface =
14824     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14825
14826   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14827   {
14828     if (!createCollectImage(i))
14829       continue;
14830
14831     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14832     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14833     int graphic = el2img(i);
14834     char *token_name = element_info[i].token_name;
14835     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14836     Bitmap *src_bitmap;
14837     int src_x, src_y;
14838
14839     Info("- creating collecting image for '%s' ...", token_name);
14840
14841     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14842
14843     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14844                tile_size, tile_size, 0, 0);
14845
14846     // force using RGBA surface for temporary bitmap (using transparent black)
14847     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14848                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14849
14850     tmp_bitmap->surface =
14851       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14852
14853     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14854
14855     for (j = 0; j < anim_frames; j++)
14856     {
14857       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14858       int frame_size = frame_size_final * num_steps;
14859       int offset = (tile_size - frame_size_final) / 2;
14860       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14861
14862       while (frame_size > frame_size_final)
14863       {
14864         frame_size /= 2;
14865
14866         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14867
14868         FreeBitmap(frame_bitmap);
14869
14870         frame_bitmap = half_bitmap;
14871       }
14872
14873       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14874                        frame_size_final, frame_size_final,
14875                        dst_x + j * tile_size + offset, dst_y + offset);
14876
14877       FreeBitmap(frame_bitmap);
14878     }
14879
14880     tmp_bitmap->surface_masked = NULL;
14881
14882     FreeBitmap(tmp_bitmap);
14883
14884     pos_collect_images++;
14885   }
14886
14887   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14888     Fail("cannot save element collecting image file '%s'", filename_bmp);
14889
14890   FreeBitmap(dst_bitmap);
14891
14892   Info("Converting image file from BMP to PNG ...");
14893
14894   if (system(cmd_convert) != 0)
14895     Fail("converting image file failed");
14896
14897   unlink(filename_bmp);
14898
14899   Info("Done.");
14900
14901   CloseAllAndExit(0);
14902 }
14903
14904
14905 // ----------------------------------------------------------------------------
14906 // create and save images for custom and group elements (raw BMP format)
14907 // ----------------------------------------------------------------------------
14908
14909 void CreateCustomElementImages(char *directory)
14910 {
14911   char *src_basename = "RocksCE-template.ilbm";
14912   char *dst_basename = "RocksCE.bmp";
14913   char *src_filename = getPath2(directory, src_basename);
14914   char *dst_filename = getPath2(directory, dst_basename);
14915   Bitmap *src_bitmap;
14916   Bitmap *bitmap;
14917   int yoffset_ce = 0;
14918   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14919   int i;
14920
14921   InitVideoDefaults();
14922
14923   ReCreateBitmap(&backbuffer, video.width, video.height);
14924
14925   src_bitmap = LoadImage(src_filename);
14926
14927   bitmap = CreateBitmap(TILEX * 16 * 2,
14928                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14929                         DEFAULT_DEPTH);
14930
14931   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14932   {
14933     int x = i % 16;
14934     int y = i / 16;
14935     int ii = i + 1;
14936     int j;
14937
14938     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14939                TILEX * x, TILEY * y + yoffset_ce);
14940
14941     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14942                TILEX, TILEY,
14943                TILEX * x + TILEX * 16,
14944                TILEY * y + yoffset_ce);
14945
14946     for (j = 2; j >= 0; j--)
14947     {
14948       int c = ii % 10;
14949
14950       BlitBitmap(src_bitmap, bitmap,
14951                  TILEX + c * 7, 0, 6, 10,
14952                  TILEX * x + 6 + j * 7,
14953                  TILEY * y + 11 + yoffset_ce);
14954
14955       BlitBitmap(src_bitmap, bitmap,
14956                  TILEX + c * 8, TILEY, 6, 10,
14957                  TILEX * 16 + TILEX * x + 6 + j * 8,
14958                  TILEY * y + 10 + yoffset_ce);
14959
14960       ii /= 10;
14961     }
14962   }
14963
14964   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14965   {
14966     int x = i % 16;
14967     int y = i / 16;
14968     int ii = i + 1;
14969     int j;
14970
14971     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14972                TILEX * x, TILEY * y + yoffset_ge);
14973
14974     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14975                TILEX, TILEY,
14976                TILEX * x + TILEX * 16,
14977                TILEY * y + yoffset_ge);
14978
14979     for (j = 1; j >= 0; j--)
14980     {
14981       int c = ii % 10;
14982
14983       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14984                  TILEX * x + 6 + j * 10,
14985                  TILEY * y + 11 + yoffset_ge);
14986
14987       BlitBitmap(src_bitmap, bitmap,
14988                  TILEX + c * 8, TILEY + 12, 6, 10,
14989                  TILEX * 16 + TILEX * x + 10 + j * 8,
14990                  TILEY * y + 10 + yoffset_ge);
14991
14992       ii /= 10;
14993     }
14994   }
14995
14996   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14997     Fail("cannot save CE graphics file '%s'", dst_filename);
14998
14999   FreeBitmap(bitmap);
15000
15001   CloseAllAndExit(0);
15002 }