added support for explosion settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
311     &li.bd_cave_random_seed_c64,        0
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (some values for BD style amoeba following below)
606   {
607     EL_BD_PLAYER,                       -1,
608     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
609     &li.bd_diagonal_movements,          FALSE
610   },
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
614     &li.bd_topmost_player_active,       TRUE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
619     &li.bd_pushing_prob,                25
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
624     &li.bd_pushing_prob_with_sweet,     100
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
629     &li.bd_push_mega_rock_with_sweet,   FALSE
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
634     &li.bd_snap_element,                EL_EMPTY
635   },
636
637   {
638     EL_BD_SAND,                         -1,
639     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
640     &li.bd_sand_looks_like,             EL_BD_SAND
641   },
642
643   {
644     EL_BD_ROCK,                         -1,
645     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
646     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
647   },
648   {
649     EL_BD_ROCK,                         -1,
650     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
651     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
652   },
653
654   {
655     EL_BD_DIAMOND,                      -1,
656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
657     &li.score[SC_DIAMOND_EXTRA],        20
658   },
659   {
660     EL_BD_DIAMOND,                      -1,
661     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
662     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
663   },
664   {
665     EL_BD_DIAMOND,                      -1,
666     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
667     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
668   },
669
670   {
671     EL_BD_FIREFLY,                      -1,
672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
673     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
674   },
675
676   {
677     EL_BD_FIREFLY_2,                    -1,
678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
679     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
680   },
681
682   {
683     EL_BD_BUTTERFLY,                    -1,
684     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
685     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
686   },
687
688   {
689     EL_BD_BUTTERFLY_2,                  -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
691     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
692   },
693
694   {
695     EL_BD_STONEFLY,                     -1,
696     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
697     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
698   },
699
700   {
701     EL_BD_DRAGONFLY,                    -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
703     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
704   },
705
706   {
707     EL_BD_MAGIC_WALL,                   -1,
708     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
709     &li.bd_magic_wall_wait_hatching,    FALSE
710   },
711   {
712     EL_BD_MAGIC_WALL,                   -1,
713     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
714     &li.bd_magic_wall_stops_amoeba,     TRUE
715   },
716   {
717     EL_BD_MAGIC_WALL,                   -1,
718     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
719     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
720   },
721   {
722     EL_BD_MAGIC_WALL,                   -1,
723     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
724     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
725   },
726   {
727     EL_BD_MAGIC_WALL,                   -1,
728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
729     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
730   },
731   {
732     EL_BD_MAGIC_WALL,                   -1,
733     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
734     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
735   },
736   {
737     EL_BD_MAGIC_WALL,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
739     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
740   },
741   {
742     EL_BD_MAGIC_WALL,                   -1,
743     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
744     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
745   },
746   {
747     EL_BD_MAGIC_WALL,                   -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
749     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
750   },
751
752   {
753     EL_BD_CLOCK,                        -1,
754     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
755     &li.bd_clock_extra_time,            30
756   },
757
758   {
759     EL_BD_VOODOO_DOLL,                  -1,
760     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
761     &li.bd_voodoo_collects_diamonds,    FALSE
762   },
763   {
764     EL_BD_VOODOO_DOLL,                  -1,
765     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
766     &li.bd_voodoo_hurt_kills_player,    FALSE
767   },
768   {
769     EL_BD_VOODOO_DOLL,                  -1,
770     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
771     &li.bd_voodoo_dies_by_rock,         FALSE
772   },
773   {
774     EL_BD_VOODOO_DOLL,                  -1,
775     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
776     &li.bd_voodoo_vanish_by_explosion,  TRUE
777   },
778   {
779     EL_BD_VOODOO_DOLL,                  -1,
780     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
781     &li.bd_voodoo_penalty_time,         30
782   },
783
784   {
785     EL_BD_SLIME,                        -1,
786     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
787     &li.bd_slime_is_predictable,        TRUE
788   },
789   {
790     EL_BD_SLIME,                        -1,
791     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
792     &li.bd_slime_permeability_rate,     100
793   },
794   {
795     EL_BD_SLIME,                        -1,
796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
797     &li.bd_slime_permeability_bits_c64, 0
798   },
799   {
800     EL_BD_SLIME,                        -1,
801     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
802     &li.bd_slime_random_seed_c64,       -1
803   },
804   {
805     EL_BD_SLIME,                        -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
808   },
809   {
810     EL_BD_SLIME,                        -1,
811     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
812     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
813   },
814   {
815     EL_BD_SLIME,                        -1,
816     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
817     &li.bd_slime_eats_element_2,        EL_BD_ROCK
818   },
819   {
820     EL_BD_SLIME,                        -1,
821     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
822     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
823   },
824   {
825     EL_BD_SLIME,                        -1,
826     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
827     &li.bd_slime_eats_element_3,        EL_BD_NUT
828   },
829   {
830     EL_BD_SLIME,                        -1,
831     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
832     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
833   },
834
835   {
836     EL_BD_ACID,                         -1,
837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
838     &li.bd_acid_eats_element,           EL_BD_SAND
839   },
840   {
841     EL_BD_ACID,                         -1,
842     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
843     &li.bd_acid_spread_rate,            3
844   },
845   {
846     EL_BD_ACID,                         -1,
847     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
848     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
849   },
850
851   {
852     EL_BD_BITER,                        -1,
853     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
854     &li.bd_biter_move_delay,            0
855   },
856   {
857     EL_BD_BITER,                        -1,
858     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
859     &li.bd_biter_eats_element,          EL_BD_DIAMOND
860   },
861
862   {
863     EL_BD_BLADDER,                      -1,
864     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
865     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
866   },
867
868   {
869     EL_BD_EXPANDABLE_WALL_ANY,          -1,
870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
871     &li.bd_change_expanding_wall,       FALSE
872   },
873   {
874     EL_BD_EXPANDABLE_WALL_ANY,          -1,
875     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
876     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
877   },
878
879   {
880     EL_BD_REPLICATOR,                   -1,
881     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
882     &li.bd_replicators_active,          TRUE
883   },
884   {
885     EL_BD_REPLICATOR,                   -1,
886     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
887     &li.bd_replicator_create_delay,     4
888   },
889
890   {
891     EL_BD_CONVEYOR_LEFT,                -1,
892     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
893     &li.bd_conveyor_belts_active,       TRUE
894   },
895   {
896     EL_BD_CONVEYOR_LEFT,                -1,
897     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
898     &li.bd_conveyor_belts_changed,      FALSE
899   },
900
901   {
902     EL_BD_WATER,                        -1,
903     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
904     &li.bd_water_cannot_flow_down,      FALSE
905   },
906
907   {
908     EL_BD_NUT,                          -1,
909     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
910     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
911   },
912
913   {
914     EL_BD_PNEUMATIC_HAMMER,             -1,
915     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
916     &li.bd_hammer_walls_break_delay,    5
917   },
918   {
919     EL_BD_PNEUMATIC_HAMMER,             -1,
920     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
921     &li.bd_hammer_walls_reappear,       FALSE
922   },
923   {
924     EL_BD_PNEUMATIC_HAMMER,             -1,
925     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
926     &li.bd_hammer_walls_reappear_delay, 100
927   },
928
929   {
930     EL_BD_SKELETON,                     -1,
931     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
932     &li.bd_num_skeletons_needed_for_pot, 5
933   },
934   {
935     EL_BD_SKELETON,                     -1,
936     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
937     &li.bd_skeleton_worth_num_diamonds, 0
938   },
939
940   {
941     EL_BD_CREATURE_SWITCH,              -1,
942     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
943     &li.bd_creatures_start_backwards,   FALSE
944   },
945   {
946     EL_BD_CREATURE_SWITCH,              -1,
947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
948     &li.bd_creatures_turn_on_hatching,  FALSE
949   },
950   {
951     EL_BD_CREATURE_SWITCH,              -1,
952     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
953     &li.bd_creatures_auto_turn_delay,   0
954   },
955
956   {
957     EL_BD_GRAVITY_SWITCH,               -1,
958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
959     &li.bd_gravity_direction,           GD_MV_DOWN
960   },
961   {
962     EL_BD_GRAVITY_SWITCH,               -1,
963     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
964     &li.bd_gravity_switch_active,       FALSE
965   },
966   {
967     EL_BD_GRAVITY_SWITCH,               -1,
968     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
969     &li.bd_gravity_switch_delay,        10
970   },
971   {
972     EL_BD_GRAVITY_SWITCH,               -1,
973     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
974     &li.bd_gravity_affects_all,         TRUE
975   },
976
977   // (the following values are related to various game elements)
978
979   {
980     EL_EMERALD,                         -1,
981     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
982     &li.score[SC_EMERALD],              10
983   },
984
985   {
986     EL_DIAMOND,                         -1,
987     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
988     &li.score[SC_DIAMOND],              10
989   },
990
991   {
992     EL_BUG,                             -1,
993     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
994     &li.score[SC_BUG],                  10
995   },
996
997   {
998     EL_SPACESHIP,                       -1,
999     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1000     &li.score[SC_SPACESHIP],            10
1001   },
1002
1003   {
1004     EL_PACMAN,                          -1,
1005     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1006     &li.score[SC_PACMAN],               10
1007   },
1008
1009   {
1010     EL_NUT,                             -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.score[SC_NUT],                  10
1013   },
1014
1015   {
1016     EL_DYNAMITE,                        -1,
1017     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1018     &li.score[SC_DYNAMITE],             10
1019   },
1020
1021   {
1022     EL_KEY_1,                           -1,
1023     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1024     &li.score[SC_KEY],                  10
1025   },
1026
1027   {
1028     EL_PEARL,                           -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.score[SC_PEARL],                10
1031   },
1032
1033   {
1034     EL_CRYSTAL,                         -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.score[SC_CRYSTAL],              10
1037   },
1038
1039   // (amoeba values used by R'n'D game engine only)
1040   {
1041     EL_BD_AMOEBA,                       -1,
1042     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1043     &li.amoeba_content,                 EL_DIAMOND
1044   },
1045   {
1046     EL_BD_AMOEBA,                       -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1048     &li.amoeba_speed,                   10
1049   },
1050   {
1051     EL_BD_AMOEBA,                       -1,
1052     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1053     &li.grow_into_diggable,             TRUE
1054   },
1055   // (amoeba values used by BD game engine only)
1056   {
1057     EL_BD_AMOEBA,                       -1,
1058     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1059     &li.bd_amoeba_wait_for_hatching,    FALSE
1060   },
1061   {
1062     EL_BD_AMOEBA,                       -1,
1063     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1064     &li.bd_amoeba_start_immediately,    TRUE
1065   },
1066   {
1067     EL_BD_AMOEBA,                       -1,
1068     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1069     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1070   },
1071   {
1072     EL_BD_AMOEBA,                       -1,
1073     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1074     &li.bd_amoeba_threshold_too_big,    200
1075   },
1076   {
1077     EL_BD_AMOEBA,                       -1,
1078     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1079     &li.bd_amoeba_slow_growth_time,     200
1080   },
1081   {
1082     EL_BD_AMOEBA,                       -1,
1083     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1084     &li.bd_amoeba_slow_growth_rate,     3
1085   },
1086   {
1087     EL_BD_AMOEBA,                       -1,
1088     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1089     &li.bd_amoeba_fast_growth_rate,     25
1090   },
1091   {
1092     EL_BD_AMOEBA,                       -1,
1093     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1094     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1095   },
1096   {
1097     EL_BD_AMOEBA,                       -1,
1098     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1099     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1100   },
1101
1102   {
1103     EL_BD_AMOEBA_2,                     -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1105     &li.bd_amoeba_2_threshold_too_big,  200
1106   },
1107   {
1108     EL_BD_AMOEBA_2,                     -1,
1109     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1110     &li.bd_amoeba_2_slow_growth_time,   200
1111   },
1112   {
1113     EL_BD_AMOEBA_2,                     -1,
1114     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1115     &li.bd_amoeba_2_slow_growth_rate,   3
1116   },
1117   {
1118     EL_BD_AMOEBA_2,                     -1,
1119     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1120     &li.bd_amoeba_2_fast_growth_rate,   25
1121   },
1122   {
1123     EL_BD_AMOEBA_2,                     -1,
1124     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1125     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1126   },
1127   {
1128     EL_BD_AMOEBA_2,                     -1,
1129     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1130     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1131   },
1132   {
1133     EL_BD_AMOEBA_2,                     -1,
1134     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1135     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1136   },
1137   {
1138     EL_BD_AMOEBA_2,                     -1,
1139     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1140     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1141   },
1142
1143   {
1144     EL_YAMYAM,                          -1,
1145     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1146     &li.yamyam_content,                 EL_ROCK, NULL,
1147     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1148   },
1149   {
1150     EL_YAMYAM,                          -1,
1151     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1152     &li.score[SC_YAMYAM],               10
1153   },
1154
1155   {
1156     EL_ROBOT,                           -1,
1157     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1158     &li.score[SC_ROBOT],                10
1159   },
1160   {
1161     EL_ROBOT,                           -1,
1162     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1163     &li.slurp_score,                    10
1164   },
1165
1166   {
1167     EL_ROBOT_WHEEL,                     -1,
1168     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1169     &li.time_wheel,                     10
1170   },
1171
1172   {
1173     EL_MAGIC_WALL,                      -1,
1174     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1175     &li.time_magic_wall,                10
1176   },
1177
1178   {
1179     EL_GAME_OF_LIFE,                    -1,
1180     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1181     &li.game_of_life[0],                2
1182   },
1183   {
1184     EL_GAME_OF_LIFE,                    -1,
1185     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1186     &li.game_of_life[1],                3
1187   },
1188   {
1189     EL_GAME_OF_LIFE,                    -1,
1190     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1191     &li.game_of_life[2],                3
1192   },
1193   {
1194     EL_GAME_OF_LIFE,                    -1,
1195     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1196     &li.game_of_life[3],                3
1197   },
1198   {
1199     EL_GAME_OF_LIFE,                    -1,
1200     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1201     &li.use_life_bugs,                  FALSE
1202   },
1203
1204   {
1205     EL_BIOMAZE,                         -1,
1206     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1207     &li.biomaze[0],                     2
1208   },
1209   {
1210     EL_BIOMAZE,                         -1,
1211     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1212     &li.biomaze[1],                     3
1213   },
1214   {
1215     EL_BIOMAZE,                         -1,
1216     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1217     &li.biomaze[2],                     3
1218   },
1219   {
1220     EL_BIOMAZE,                         -1,
1221     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1222     &li.biomaze[3],                     3
1223   },
1224
1225   {
1226     EL_TIMEGATE_SWITCH,                 -1,
1227     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1228     &li.time_timegate,                  10
1229   },
1230
1231   {
1232     EL_LIGHT_SWITCH_ACTIVE,             -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1234     &li.time_light,                     10
1235   },
1236
1237   {
1238     EL_SHIELD_NORMAL,                   -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1240     &li.shield_normal_time,             10
1241   },
1242   {
1243     EL_SHIELD_NORMAL,                   -1,
1244     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1245     &li.score[SC_SHIELD],               10
1246   },
1247
1248   {
1249     EL_SHIELD_DEADLY,                   -1,
1250     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1251     &li.shield_deadly_time,             10
1252   },
1253   {
1254     EL_SHIELD_DEADLY,                   -1,
1255     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1256     &li.score[SC_SHIELD],               10
1257   },
1258
1259   {
1260     EL_EXTRA_TIME,                      -1,
1261     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1262     &li.extra_time,                     10
1263   },
1264   {
1265     EL_EXTRA_TIME,                      -1,
1266     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1267     &li.extra_time_score,               10
1268   },
1269
1270   {
1271     EL_TIME_ORB_FULL,                   -1,
1272     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1273     &li.time_orb_time,                  10
1274   },
1275   {
1276     EL_TIME_ORB_FULL,                   -1,
1277     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1278     &li.use_time_orb_bug,               FALSE
1279   },
1280
1281   {
1282     EL_SPRING,                          -1,
1283     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1284     &li.use_spring_bug,                 FALSE
1285   },
1286
1287   {
1288     EL_EMC_ANDROID,                     -1,
1289     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1290     &li.android_move_time,              10
1291   },
1292   {
1293     EL_EMC_ANDROID,                     -1,
1294     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1295     &li.android_clone_time,             10
1296   },
1297   {
1298     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1299     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1300     &li.android_clone_element[0],       EL_EMPTY, NULL,
1301     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1302   },
1303   {
1304     EL_EMC_ANDROID,                     -1,
1305     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1306     &li.android_clone_element[0],       EL_EMPTY, NULL,
1307     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1308   },
1309
1310   {
1311     EL_EMC_LENSES,                      -1,
1312     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1313     &li.lenses_score,                   10
1314   },
1315   {
1316     EL_EMC_LENSES,                      -1,
1317     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1318     &li.lenses_time,                    10
1319   },
1320
1321   {
1322     EL_EMC_MAGNIFIER,                   -1,
1323     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1324     &li.magnify_score,                  10
1325   },
1326   {
1327     EL_EMC_MAGNIFIER,                   -1,
1328     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1329     &li.magnify_time,                   10
1330   },
1331
1332   {
1333     EL_EMC_MAGIC_BALL,                  -1,
1334     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1335     &li.ball_time,                      10
1336   },
1337   {
1338     EL_EMC_MAGIC_BALL,                  -1,
1339     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1340     &li.ball_random,                    FALSE
1341   },
1342   {
1343     EL_EMC_MAGIC_BALL,                  -1,
1344     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1345     &li.ball_active_initial,            FALSE
1346   },
1347   {
1348     EL_EMC_MAGIC_BALL,                  -1,
1349     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1350     &li.ball_content,                   EL_EMPTY, NULL,
1351     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1352   },
1353
1354   {
1355     EL_SOKOBAN_FIELD_EMPTY,             -1,
1356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1357     &li.sb_fields_needed,               TRUE
1358   },
1359
1360   {
1361     EL_SOKOBAN_OBJECT,                  -1,
1362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1363     &li.sb_objects_needed,              TRUE
1364   },
1365
1366   {
1367     EL_MM_MCDUFFIN,                     -1,
1368     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1369     &li.mm_laser_red,                   FALSE
1370   },
1371   {
1372     EL_MM_MCDUFFIN,                     -1,
1373     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1374     &li.mm_laser_green,                 FALSE
1375   },
1376   {
1377     EL_MM_MCDUFFIN,                     -1,
1378     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1379     &li.mm_laser_blue,                  TRUE
1380   },
1381
1382   {
1383     EL_DF_LASER,                        -1,
1384     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1385     &li.df_laser_red,                   TRUE
1386   },
1387   {
1388     EL_DF_LASER,                        -1,
1389     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1390     &li.df_laser_green,                 TRUE
1391   },
1392   {
1393     EL_DF_LASER,                        -1,
1394     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1395     &li.df_laser_blue,                  FALSE
1396   },
1397
1398   {
1399     EL_MM_FUSE_ACTIVE,                  -1,
1400     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1401     &li.mm_time_fuse,                   25
1402   },
1403   {
1404     EL_MM_BOMB,                         -1,
1405     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1406     &li.mm_time_bomb,                   75
1407   },
1408
1409   {
1410     EL_MM_GRAY_BALL,                    -1,
1411     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1412     &li.mm_time_ball,                   75
1413   },
1414   {
1415     EL_MM_GRAY_BALL,                    -1,
1416     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1417     &li.mm_ball_choice_mode,            ANIM_RANDOM
1418   },
1419   {
1420     EL_MM_GRAY_BALL,                    -1,
1421     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1422     &li.mm_ball_content,                EL_EMPTY, NULL,
1423     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1424   },
1425   {
1426     EL_MM_GRAY_BALL,                    -1,
1427     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1428     &li.rotate_mm_ball_content,         TRUE
1429   },
1430   {
1431     EL_MM_GRAY_BALL,                    -1,
1432     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1433     &li.explode_mm_ball,                FALSE
1434   },
1435
1436   {
1437     EL_MM_STEEL_BLOCK,                  -1,
1438     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1439     &li.mm_time_block,                  75
1440   },
1441   {
1442     EL_MM_LIGHTBALL,                    -1,
1443     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1444     &li.score[SC_ELEM_BONUS],           10
1445   },
1446
1447   {
1448     -1,                                 -1,
1449     -1,                                 -1,
1450     NULL,                               -1
1451   }
1452 };
1453
1454 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1455 {
1456   {
1457     -1,                                 -1,
1458     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1459     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1460   },
1461   {
1462     -1,                                 -1,
1463     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1464     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1465   },
1466
1467   {
1468     -1,                                 -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1470     &xx_envelope.autowrap,              FALSE
1471   },
1472   {
1473     -1,                                 -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1475     &xx_envelope.centered,              FALSE
1476   },
1477
1478   {
1479     -1,                                 -1,
1480     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1481     &xx_envelope.text,                  -1, NULL,
1482     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1483     &xx_default_string_empty[0]
1484   },
1485
1486   {
1487     -1,                                 -1,
1488     -1,                                 -1,
1489     NULL,                               -1
1490   }
1491 };
1492
1493 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1494 {
1495   {
1496     -1,                                 -1,
1497     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1498     &xx_ei.description[0],              -1,
1499     &yy_ei.description[0],
1500     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1501     &xx_default_description[0]
1502   },
1503
1504   {
1505     -1,                                 -1,
1506     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1507     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1508     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1509   },
1510 #if ENABLE_RESERVED_CODE
1511   // (reserved for later use)
1512   {
1513     -1,                                 -1,
1514     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1515     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1516     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1517   },
1518 #endif
1519
1520   {
1521     -1,                                 -1,
1522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1523     &xx_ei.use_gfx_element,             FALSE,
1524     &yy_ei.use_gfx_element
1525   },
1526   {
1527     -1,                                 -1,
1528     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1529     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1530     &yy_ei.gfx_element_initial
1531   },
1532
1533   {
1534     -1,                                 -1,
1535     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1536     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1537     &yy_ei.access_direction
1538   },
1539
1540   {
1541     -1,                                 -1,
1542     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1543     &xx_ei.collect_score_initial,       10,
1544     &yy_ei.collect_score_initial
1545   },
1546   {
1547     -1,                                 -1,
1548     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1549     &xx_ei.collect_count_initial,       1,
1550     &yy_ei.collect_count_initial
1551   },
1552
1553   {
1554     -1,                                 -1,
1555     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1556     &xx_ei.ce_value_fixed_initial,      0,
1557     &yy_ei.ce_value_fixed_initial
1558   },
1559   {
1560     -1,                                 -1,
1561     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1562     &xx_ei.ce_value_random_initial,     0,
1563     &yy_ei.ce_value_random_initial
1564   },
1565   {
1566     -1,                                 -1,
1567     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1568     &xx_ei.use_last_ce_value,           FALSE,
1569     &yy_ei.use_last_ce_value
1570   },
1571
1572   {
1573     -1,                                 -1,
1574     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1575     &xx_ei.push_delay_fixed,            8,
1576     &yy_ei.push_delay_fixed
1577   },
1578   {
1579     -1,                                 -1,
1580     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1581     &xx_ei.push_delay_random,           8,
1582     &yy_ei.push_delay_random
1583   },
1584   {
1585     -1,                                 -1,
1586     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1587     &xx_ei.drop_delay_fixed,            0,
1588     &yy_ei.drop_delay_fixed
1589   },
1590   {
1591     -1,                                 -1,
1592     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1593     &xx_ei.drop_delay_random,           0,
1594     &yy_ei.drop_delay_random
1595   },
1596   {
1597     -1,                                 -1,
1598     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1599     &xx_ei.move_delay_fixed,            0,
1600     &yy_ei.move_delay_fixed
1601   },
1602   {
1603     -1,                                 -1,
1604     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1605     &xx_ei.move_delay_random,           0,
1606     &yy_ei.move_delay_random
1607   },
1608   {
1609     -1,                                 -1,
1610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1611     &xx_ei.step_delay_fixed,            0,
1612     &yy_ei.step_delay_fixed
1613   },
1614   {
1615     -1,                                 -1,
1616     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1617     &xx_ei.step_delay_random,           0,
1618     &yy_ei.step_delay_random
1619   },
1620
1621   {
1622     -1,                                 -1,
1623     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1624     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1625     &yy_ei.move_pattern
1626   },
1627   {
1628     -1,                                 -1,
1629     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1630     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1631     &yy_ei.move_direction_initial
1632   },
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1636     &xx_ei.move_stepsize,               TILEX / 8,
1637     &yy_ei.move_stepsize
1638   },
1639
1640   {
1641     -1,                                 -1,
1642     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1643     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1644     &yy_ei.move_enter_element
1645   },
1646   {
1647     -1,                                 -1,
1648     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1649     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1650     &yy_ei.move_leave_element
1651   },
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1655     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1656     &yy_ei.move_leave_type
1657   },
1658
1659   {
1660     -1,                                 -1,
1661     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1662     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1663     &yy_ei.slippery_type
1664   },
1665
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1669     &xx_ei.explosion_type,              EXPLODES_3X3,
1670     &yy_ei.explosion_type
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1675     &xx_ei.explosion_delay,             16,
1676     &yy_ei.explosion_delay
1677   },
1678   {
1679     -1,                                 -1,
1680     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1681     &xx_ei.ignition_delay,              8,
1682     &yy_ei.ignition_delay
1683   },
1684
1685   {
1686     -1,                                 -1,
1687     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1688     &xx_ei.content,                     EL_EMPTY_SPACE,
1689     &yy_ei.content,
1690     &xx_num_contents,                   1, 1
1691   },
1692
1693   // ---------- "num_change_pages" must be the last entry ---------------------
1694
1695   {
1696     -1,                                 SAVE_CONF_ALWAYS,
1697     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1698     &xx_ei.num_change_pages,            1,
1699     &yy_ei.num_change_pages
1700   },
1701
1702   {
1703     -1,                                 -1,
1704     -1,                                 -1,
1705     NULL,                               -1,
1706     NULL
1707   }
1708 };
1709
1710 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1711 {
1712   // ---------- "current_change_page" must be the first entry -----------------
1713
1714   {
1715     -1,                                 SAVE_CONF_ALWAYS,
1716     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1717     &xx_current_change_page,            -1
1718   },
1719
1720   // ---------- (the remaining entries can be in any order) -------------------
1721
1722   {
1723     -1,                                 -1,
1724     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1725     &xx_change.can_change,              FALSE
1726   },
1727
1728   {
1729     -1,                                 -1,
1730     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1731     &xx_event_bits[0],                  0
1732   },
1733   {
1734     -1,                                 -1,
1735     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1736     &xx_event_bits[1],                  0
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1742     &xx_change.trigger_player,          CH_PLAYER_ANY
1743   },
1744   {
1745     -1,                                 -1,
1746     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1747     &xx_change.trigger_side,            CH_SIDE_ANY
1748   },
1749   {
1750     -1,                                 -1,
1751     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1752     &xx_change.trigger_page,            CH_PAGE_ANY
1753   },
1754
1755   {
1756     -1,                                 -1,
1757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1758     &xx_change.target_element,          EL_EMPTY_SPACE
1759   },
1760
1761   {
1762     -1,                                 -1,
1763     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1764     &xx_change.delay_fixed,             0
1765   },
1766   {
1767     -1,                                 -1,
1768     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1769     &xx_change.delay_random,            0
1770   },
1771   {
1772     -1,                                 -1,
1773     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1774     &xx_change.delay_frames,            FRAMES_PER_SECOND
1775   },
1776
1777   {
1778     -1,                                 -1,
1779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1780     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1781   },
1782
1783   {
1784     -1,                                 -1,
1785     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1786     &xx_change.explode,                 FALSE
1787   },
1788   {
1789     -1,                                 -1,
1790     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1791     &xx_change.use_target_content,      FALSE
1792   },
1793   {
1794     -1,                                 -1,
1795     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1796     &xx_change.only_if_complete,        FALSE
1797   },
1798   {
1799     -1,                                 -1,
1800     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1801     &xx_change.use_random_replace,      FALSE
1802   },
1803   {
1804     -1,                                 -1,
1805     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1806     &xx_change.random_percentage,       100
1807   },
1808   {
1809     -1,                                 -1,
1810     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1811     &xx_change.replace_when,            CP_WHEN_EMPTY
1812   },
1813
1814   {
1815     -1,                                 -1,
1816     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1817     &xx_change.has_action,              FALSE
1818   },
1819   {
1820     -1,                                 -1,
1821     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1822     &xx_change.action_type,             CA_NO_ACTION
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1827     &xx_change.action_mode,             CA_MODE_UNDEFINED
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1832     &xx_change.action_arg,              CA_ARG_UNDEFINED
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1838     &xx_change.action_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1844     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1845     &xx_num_contents,                   1, 1
1846   },
1847
1848   {
1849     -1,                                 -1,
1850     -1,                                 -1,
1851     NULL,                               -1
1852   }
1853 };
1854
1855 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1856 {
1857   {
1858     -1,                                 -1,
1859     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1860     &xx_ei.description[0],              -1, NULL,
1861     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1862     &xx_default_description[0]
1863   },
1864
1865   {
1866     -1,                                 -1,
1867     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1868     &xx_ei.use_gfx_element,             FALSE
1869   },
1870   {
1871     -1,                                 -1,
1872     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1873     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1874   },
1875
1876   {
1877     -1,                                 -1,
1878     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1879     &xx_group.choice_mode,              ANIM_RANDOM
1880   },
1881
1882   {
1883     -1,                                 -1,
1884     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1885     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1886     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1887   },
1888
1889   {
1890     -1,                                 -1,
1891     -1,                                 -1,
1892     NULL,                               -1
1893   }
1894 };
1895
1896 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1897 {
1898   {
1899     -1,                                 -1,
1900     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1901     &xx_ei.use_gfx_element,             FALSE
1902   },
1903   {
1904     -1,                                 -1,
1905     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1906     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1907   },
1908
1909   {
1910     -1,                                 -1,
1911     -1,                                 -1,
1912     NULL,                               -1
1913   }
1914 };
1915
1916 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1917 {
1918   {
1919     EL_PLAYER_1,                        -1,
1920     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1921     &li.block_snap_field,               TRUE
1922   },
1923   {
1924     EL_PLAYER_1,                        -1,
1925     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1926     &li.continuous_snapping,            TRUE
1927   },
1928   {
1929     EL_PLAYER_1,                        -1,
1930     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1931     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1932   },
1933   {
1934     EL_PLAYER_1,                        -1,
1935     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1936     &li.use_start_element[0],           FALSE
1937   },
1938   {
1939     EL_PLAYER_1,                        -1,
1940     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1941     &li.start_element[0],               EL_PLAYER_1
1942   },
1943   {
1944     EL_PLAYER_1,                        -1,
1945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1946     &li.use_artwork_element[0],         FALSE
1947   },
1948   {
1949     EL_PLAYER_1,                        -1,
1950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1951     &li.artwork_element[0],             EL_PLAYER_1
1952   },
1953   {
1954     EL_PLAYER_1,                        -1,
1955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1956     &li.use_explosion_element[0],       FALSE
1957   },
1958   {
1959     EL_PLAYER_1,                        -1,
1960     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1961     &li.explosion_element[0],           EL_PLAYER_1
1962   },
1963
1964   {
1965     -1,                                 -1,
1966     -1,                                 -1,
1967     NULL,                               -1
1968   }
1969 };
1970
1971 static struct
1972 {
1973   int filetype;
1974   char *id;
1975 }
1976 filetype_id_list[] =
1977 {
1978   { LEVEL_FILE_TYPE_RND,        "RND"   },
1979   { LEVEL_FILE_TYPE_BD,         "BD"    },
1980   { LEVEL_FILE_TYPE_EM,         "EM"    },
1981   { LEVEL_FILE_TYPE_SP,         "SP"    },
1982   { LEVEL_FILE_TYPE_DX,         "DX"    },
1983   { LEVEL_FILE_TYPE_SB,         "SB"    },
1984   { LEVEL_FILE_TYPE_DC,         "DC"    },
1985   { LEVEL_FILE_TYPE_MM,         "MM"    },
1986   { LEVEL_FILE_TYPE_MM,         "DF"    },
1987   { -1,                         NULL    },
1988 };
1989
1990
1991 // ============================================================================
1992 // level file functions
1993 // ============================================================================
1994
1995 static boolean check_special_flags(char *flag)
1996 {
1997   if (strEqual(options.special_flags, flag) ||
1998       strEqual(leveldir_current->special_flags, flag))
1999     return TRUE;
2000
2001   return FALSE;
2002 }
2003
2004 static struct DateInfo getCurrentDate(void)
2005 {
2006   time_t epoch_seconds = time(NULL);
2007   struct tm *now = localtime(&epoch_seconds);
2008   struct DateInfo date;
2009
2010   date.year  = now->tm_year + 1900;
2011   date.month = now->tm_mon  + 1;
2012   date.day   = now->tm_mday;
2013
2014   date.src   = DATE_SRC_CLOCK;
2015
2016   return date;
2017 }
2018
2019 static void resetEventFlags(struct ElementChangeInfo *change)
2020 {
2021   int i;
2022
2023   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2024     change->has_event[i] = FALSE;
2025 }
2026
2027 static void resetEventBits(void)
2028 {
2029   int i;
2030
2031   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2032     xx_event_bits[i] = 0;
2033 }
2034
2035 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2036 {
2037   int i;
2038
2039   /* important: only change event flag if corresponding event bit is set
2040      (this is because all xx_event_bits[] values are loaded separately,
2041      and all xx_event_bits[] values are set back to zero before loading
2042      another value xx_event_bits[x] (each value representing 32 flags)) */
2043
2044   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2045     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2046       change->has_event[i] = TRUE;
2047 }
2048
2049 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2050 {
2051   int i;
2052
2053   /* in contrast to the above function setEventFlagsFromEventBits(), it
2054      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2055      depending on the corresponding change->has_event[i] values here, as
2056      all xx_event_bits[] values are reset in resetEventBits() before */
2057
2058   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2059     if (change->has_event[i])
2060       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2061 }
2062
2063 static char *getDefaultElementDescription(struct ElementInfo *ei)
2064 {
2065   static char description[MAX_ELEMENT_NAME_LEN + 1];
2066   char *default_description = (ei->custom_description != NULL ?
2067                                ei->custom_description :
2068                                ei->editor_description);
2069   int i;
2070
2071   // always start with reliable default values
2072   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2073     description[i] = '\0';
2074
2075   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2076   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2077
2078   return &description[0];
2079 }
2080
2081 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2082 {
2083   char *default_description = getDefaultElementDescription(ei);
2084   int i;
2085
2086   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2087     ei->description[i] = default_description[i];
2088 }
2089
2090 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2091 {
2092   int i;
2093
2094   for (i = 0; conf[i].data_type != -1; i++)
2095   {
2096     int default_value = conf[i].default_value;
2097     int data_type = conf[i].data_type;
2098     int conf_type = conf[i].conf_type;
2099     int byte_mask = conf_type & CONF_MASK_BYTES;
2100
2101     if (byte_mask == CONF_MASK_MULTI_BYTES)
2102     {
2103       int default_num_entities = conf[i].default_num_entities;
2104       int max_num_entities = conf[i].max_num_entities;
2105
2106       *(int *)(conf[i].num_entities) = default_num_entities;
2107
2108       if (data_type == TYPE_STRING)
2109       {
2110         char *default_string = conf[i].default_string;
2111         char *string = (char *)(conf[i].value);
2112
2113         strncpy(string, default_string, max_num_entities);
2114       }
2115       else if (data_type == TYPE_ELEMENT_LIST)
2116       {
2117         int *element_array = (int *)(conf[i].value);
2118         int j;
2119
2120         for (j = 0; j < max_num_entities; j++)
2121           element_array[j] = default_value;
2122       }
2123       else if (data_type == TYPE_CONTENT_LIST)
2124       {
2125         struct Content *content = (struct Content *)(conf[i].value);
2126         int c, x, y;
2127
2128         for (c = 0; c < max_num_entities; c++)
2129           for (y = 0; y < 3; y++)
2130             for (x = 0; x < 3; x++)
2131               content[c].e[x][y] = default_value;
2132       }
2133     }
2134     else        // constant size configuration data (1, 2 or 4 bytes)
2135     {
2136       if (data_type == TYPE_BOOLEAN)
2137         *(boolean *)(conf[i].value) = default_value;
2138       else
2139         *(int *)    (conf[i].value) = default_value;
2140     }
2141   }
2142 }
2143
2144 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2145 {
2146   int i;
2147
2148   for (i = 0; conf[i].data_type != -1; i++)
2149   {
2150     int data_type = conf[i].data_type;
2151     int conf_type = conf[i].conf_type;
2152     int byte_mask = conf_type & CONF_MASK_BYTES;
2153
2154     if (byte_mask == CONF_MASK_MULTI_BYTES)
2155     {
2156       int max_num_entities = conf[i].max_num_entities;
2157
2158       if (data_type == TYPE_STRING)
2159       {
2160         char *string      = (char *)(conf[i].value);
2161         char *string_copy = (char *)(conf[i].value_copy);
2162
2163         strncpy(string_copy, string, max_num_entities);
2164       }
2165       else if (data_type == TYPE_ELEMENT_LIST)
2166       {
2167         int *element_array      = (int *)(conf[i].value);
2168         int *element_array_copy = (int *)(conf[i].value_copy);
2169         int j;
2170
2171         for (j = 0; j < max_num_entities; j++)
2172           element_array_copy[j] = element_array[j];
2173       }
2174       else if (data_type == TYPE_CONTENT_LIST)
2175       {
2176         struct Content *content      = (struct Content *)(conf[i].value);
2177         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2178         int c, x, y;
2179
2180         for (c = 0; c < max_num_entities; c++)
2181           for (y = 0; y < 3; y++)
2182             for (x = 0; x < 3; x++)
2183               content_copy[c].e[x][y] = content[c].e[x][y];
2184       }
2185     }
2186     else        // constant size configuration data (1, 2 or 4 bytes)
2187     {
2188       if (data_type == TYPE_BOOLEAN)
2189         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2190       else
2191         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2192     }
2193   }
2194 }
2195
2196 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2197 {
2198   int i;
2199
2200   xx_ei = *ei_from;     // copy element data into temporary buffer
2201   yy_ei = *ei_to;       // copy element data into temporary buffer
2202
2203   copyConfigFromConfigList(chunk_config_CUSX_base);
2204
2205   *ei_from = xx_ei;
2206   *ei_to   = yy_ei;
2207
2208   // ---------- reinitialize and copy change pages ----------
2209
2210   ei_to->num_change_pages = ei_from->num_change_pages;
2211   ei_to->current_change_page = ei_from->current_change_page;
2212
2213   setElementChangePages(ei_to, ei_to->num_change_pages);
2214
2215   for (i = 0; i < ei_to->num_change_pages; i++)
2216     ei_to->change_page[i] = ei_from->change_page[i];
2217
2218   // ---------- copy group element info ----------
2219   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2220     *ei_to->group = *ei_from->group;
2221
2222   // mark this custom element as modified
2223   ei_to->modified_settings = TRUE;
2224 }
2225
2226 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2227 {
2228   int change_page_size = sizeof(struct ElementChangeInfo);
2229
2230   ei->num_change_pages = MAX(1, change_pages);
2231
2232   ei->change_page =
2233     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2234
2235   if (ei->current_change_page >= ei->num_change_pages)
2236     ei->current_change_page = ei->num_change_pages - 1;
2237
2238   ei->change = &ei->change_page[ei->current_change_page];
2239 }
2240
2241 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2242 {
2243   xx_change = *change;          // copy change data into temporary buffer
2244
2245   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2246
2247   *change = xx_change;
2248
2249   resetEventFlags(change);
2250
2251   change->direct_action = 0;
2252   change->other_action = 0;
2253
2254   change->pre_change_function = NULL;
2255   change->change_function = NULL;
2256   change->post_change_function = NULL;
2257 }
2258
2259 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2260 {
2261   int i, x, y;
2262
2263   li = *level;          // copy level data into temporary buffer
2264   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2265   *level = li;          // copy temporary buffer back to level data
2266
2267   setLevelInfoToDefaults_BD();
2268   setLevelInfoToDefaults_EM();
2269   setLevelInfoToDefaults_SP();
2270   setLevelInfoToDefaults_MM();
2271
2272   level->native_bd_level = &native_bd_level;
2273   level->native_em_level = &native_em_level;
2274   level->native_sp_level = &native_sp_level;
2275   level->native_mm_level = &native_mm_level;
2276
2277   level->file_version = FILE_VERSION_ACTUAL;
2278   level->game_version = GAME_VERSION_ACTUAL;
2279
2280   level->creation_date = getCurrentDate();
2281
2282   level->encoding_16bit_field  = TRUE;
2283   level->encoding_16bit_yamyam = TRUE;
2284   level->encoding_16bit_amoeba = TRUE;
2285
2286   // clear level name and level author string buffers
2287   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2288     level->name[i] = '\0';
2289   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2290     level->author[i] = '\0';
2291
2292   // set level name and level author to default values
2293   strcpy(level->name, NAMELESS_LEVEL_NAME);
2294   strcpy(level->author, ANONYMOUS_NAME);
2295
2296   // set level playfield to playable default level with player and exit
2297   for (x = 0; x < MAX_LEV_FIELDX; x++)
2298     for (y = 0; y < MAX_LEV_FIELDY; y++)
2299       level->field[x][y] = EL_SAND;
2300
2301   level->field[0][0] = EL_PLAYER_1;
2302   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2303
2304   BorderElement = EL_STEELWALL;
2305
2306   // detect custom elements when loading them
2307   level->file_has_custom_elements = FALSE;
2308
2309   // set all bug compatibility flags to "false" => do not emulate this bug
2310   level->use_action_after_change_bug = FALSE;
2311
2312   if (leveldir_current)
2313   {
2314     // try to determine better author name than 'anonymous'
2315     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2316     {
2317       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2318       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2319     }
2320     else
2321     {
2322       switch (LEVELCLASS(leveldir_current))
2323       {
2324         case LEVELCLASS_TUTORIAL:
2325           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2326           break;
2327
2328         case LEVELCLASS_CONTRIB:
2329           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2330           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2331           break;
2332
2333         case LEVELCLASS_PRIVATE:
2334           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2335           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2336           break;
2337
2338         default:
2339           // keep default value
2340           break;
2341       }
2342     }
2343   }
2344 }
2345
2346 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2347 {
2348   static boolean clipboard_elements_initialized = FALSE;
2349   int i;
2350
2351   InitElementPropertiesStatic();
2352
2353   li = *level;          // copy level data into temporary buffer
2354   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2355   *level = li;          // copy temporary buffer back to level data
2356
2357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2358   {
2359     int element = i;
2360     struct ElementInfo *ei = &element_info[element];
2361
2362     if (element == EL_MM_GRAY_BALL)
2363     {
2364       struct LevelInfo_MM *level_mm = level->native_mm_level;
2365       int j;
2366
2367       for (j = 0; j < level->num_mm_ball_contents; j++)
2368         level->mm_ball_content[j] =
2369           map_element_MM_to_RND(level_mm->ball_content[j]);
2370     }
2371
2372     // never initialize clipboard elements after the very first time
2373     // (to be able to use clipboard elements between several levels)
2374     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2375       continue;
2376
2377     if (IS_ENVELOPE(element))
2378     {
2379       int envelope_nr = element - EL_ENVELOPE_1;
2380
2381       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2382
2383       level->envelope[envelope_nr] = xx_envelope;
2384     }
2385
2386     if (IS_CUSTOM_ELEMENT(element) ||
2387         IS_GROUP_ELEMENT(element) ||
2388         IS_INTERNAL_ELEMENT(element))
2389     {
2390       xx_ei = *ei;      // copy element data into temporary buffer
2391
2392       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2393
2394       *ei = xx_ei;
2395     }
2396
2397     setElementChangePages(ei, 1);
2398     setElementChangeInfoToDefaults(ei->change);
2399
2400     if (IS_CUSTOM_ELEMENT(element) ||
2401         IS_GROUP_ELEMENT(element))
2402     {
2403       setElementDescriptionToDefault(ei);
2404
2405       ei->modified_settings = FALSE;
2406     }
2407
2408     if (IS_CUSTOM_ELEMENT(element) ||
2409         IS_INTERNAL_ELEMENT(element))
2410     {
2411       // internal values used in level editor
2412
2413       ei->access_type = 0;
2414       ei->access_layer = 0;
2415       ei->access_protected = 0;
2416       ei->walk_to_action = 0;
2417       ei->smash_targets = 0;
2418       ei->deadliness = 0;
2419
2420       ei->can_explode_by_fire = FALSE;
2421       ei->can_explode_smashed = FALSE;
2422       ei->can_explode_impact = FALSE;
2423
2424       ei->current_change_page = 0;
2425     }
2426
2427     if (IS_GROUP_ELEMENT(element) ||
2428         IS_INTERNAL_ELEMENT(element))
2429     {
2430       struct ElementGroupInfo *group;
2431
2432       // initialize memory for list of elements in group
2433       if (ei->group == NULL)
2434         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2435
2436       group = ei->group;
2437
2438       xx_group = *group;        // copy group data into temporary buffer
2439
2440       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2441
2442       *group = xx_group;
2443     }
2444
2445     if (IS_EMPTY_ELEMENT(element) ||
2446         IS_INTERNAL_ELEMENT(element))
2447     {
2448       xx_ei = *ei;              // copy element data into temporary buffer
2449
2450       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2451
2452       *ei = xx_ei;
2453     }
2454   }
2455
2456   clipboard_elements_initialized = TRUE;
2457 }
2458
2459 static void setLevelInfoToDefaults(struct LevelInfo *level,
2460                                    boolean level_info_only,
2461                                    boolean reset_file_status)
2462 {
2463   setLevelInfoToDefaults_Level(level);
2464
2465   if (!level_info_only)
2466     setLevelInfoToDefaults_Elements(level);
2467
2468   if (reset_file_status)
2469   {
2470     level->no_valid_file = FALSE;
2471     level->no_level_file = FALSE;
2472   }
2473
2474   level->changed = FALSE;
2475 }
2476
2477 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2478 {
2479   level_file_info->nr = 0;
2480   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2481   level_file_info->packed = FALSE;
2482
2483   setString(&level_file_info->basename, NULL);
2484   setString(&level_file_info->filename, NULL);
2485 }
2486
2487 int getMappedElement_SB(int, boolean);
2488
2489 static void ActivateLevelTemplate(void)
2490 {
2491   int x, y;
2492
2493   if (check_special_flags("load_xsb_to_ces"))
2494   {
2495     // fill smaller playfields with padding "beyond border wall" elements
2496     if (level.fieldx < level_template.fieldx ||
2497         level.fieldy < level_template.fieldy)
2498     {
2499       short field[level.fieldx][level.fieldy];
2500       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2501       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2502       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2503       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2504
2505       // copy old playfield (which is smaller than the visible area)
2506       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2507         field[x][y] = level.field[x][y];
2508
2509       // fill new, larger playfield with "beyond border wall" elements
2510       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2511         level.field[x][y] = getMappedElement_SB('_', TRUE);
2512
2513       // copy the old playfield to the middle of the new playfield
2514       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2515         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2516
2517       level.fieldx = new_fieldx;
2518       level.fieldy = new_fieldy;
2519     }
2520   }
2521
2522   // Currently there is no special action needed to activate the template
2523   // data, because 'element_info' property settings overwrite the original
2524   // level data, while all other variables do not change.
2525
2526   // Exception: 'from_level_template' elements in the original level playfield
2527   // are overwritten with the corresponding elements at the same position in
2528   // playfield from the level template.
2529
2530   for (x = 0; x < level.fieldx; x++)
2531     for (y = 0; y < level.fieldy; y++)
2532       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2533         level.field[x][y] = level_template.field[x][y];
2534
2535   if (check_special_flags("load_xsb_to_ces"))
2536   {
2537     struct LevelInfo level_backup = level;
2538
2539     // overwrite all individual level settings from template level settings
2540     level = level_template;
2541
2542     // restore level file info
2543     level.file_info = level_backup.file_info;
2544
2545     // restore playfield size
2546     level.fieldx = level_backup.fieldx;
2547     level.fieldy = level_backup.fieldy;
2548
2549     // restore playfield content
2550     for (x = 0; x < level.fieldx; x++)
2551       for (y = 0; y < level.fieldy; y++)
2552         level.field[x][y] = level_backup.field[x][y];
2553
2554     // restore name and author from individual level
2555     strcpy(level.name,   level_backup.name);
2556     strcpy(level.author, level_backup.author);
2557
2558     // restore flag "use_custom_template"
2559     level.use_custom_template = level_backup.use_custom_template;
2560   }
2561 }
2562
2563 static boolean checkForPackageFromBasename_BD(char *basename)
2564 {
2565   // check for native BD level file extensions
2566   if (!strSuffixLower(basename, ".bd") &&
2567       !strSuffixLower(basename, ".bdr") &&
2568       !strSuffixLower(basename, ".brc") &&
2569       !strSuffixLower(basename, ".gds"))
2570     return FALSE;
2571
2572   // check for standard single-level BD files (like "001.bd")
2573   if (strSuffixLower(basename, ".bd") &&
2574       strlen(basename) == 6 &&
2575       basename[0] >= '0' && basename[0] <= '9' &&
2576       basename[1] >= '0' && basename[1] <= '9' &&
2577       basename[2] >= '0' && basename[2] <= '9')
2578     return FALSE;
2579
2580   // this is a level package in native BD file format
2581   return TRUE;
2582 }
2583
2584 static char *getLevelFilenameFromBasename(char *basename)
2585 {
2586   static char *filename = NULL;
2587
2588   checked_free(filename);
2589
2590   filename = getPath2(getCurrentLevelDir(), basename);
2591
2592   return filename;
2593 }
2594
2595 static int getFileTypeFromBasename(char *basename)
2596 {
2597   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2598
2599   static char *filename = NULL;
2600   struct stat file_status;
2601
2602   // ---------- try to determine file type from filename ----------
2603
2604   // check for typical filename of a Supaplex level package file
2605   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2606     return LEVEL_FILE_TYPE_SP;
2607
2608   // check for typical filename of a Diamond Caves II level package file
2609   if (strSuffixLower(basename, ".dc") ||
2610       strSuffixLower(basename, ".dc2"))
2611     return LEVEL_FILE_TYPE_DC;
2612
2613   // check for typical filename of a Sokoban level package file
2614   if (strSuffixLower(basename, ".xsb") &&
2615       strchr(basename, '%') == NULL)
2616     return LEVEL_FILE_TYPE_SB;
2617
2618   // check for typical filename of a Boulder Dash (GDash) level package file
2619   if (checkForPackageFromBasename_BD(basename))
2620     return LEVEL_FILE_TYPE_BD;
2621
2622   // ---------- try to determine file type from filesize ----------
2623
2624   checked_free(filename);
2625   filename = getPath2(getCurrentLevelDir(), basename);
2626
2627   if (stat(filename, &file_status) == 0)
2628   {
2629     // check for typical filesize of a Supaplex level package file
2630     if (file_status.st_size == 170496)
2631       return LEVEL_FILE_TYPE_SP;
2632   }
2633
2634   return LEVEL_FILE_TYPE_UNKNOWN;
2635 }
2636
2637 static int getFileTypeFromMagicBytes(char *filename, int type)
2638 {
2639   File *file;
2640
2641   if ((file = openFile(filename, MODE_READ)))
2642   {
2643     char chunk_name[CHUNK_ID_LEN + 1];
2644
2645     getFileChunkBE(file, chunk_name, NULL);
2646
2647     if (strEqual(chunk_name, "MMII") ||
2648         strEqual(chunk_name, "MIRR"))
2649       type = LEVEL_FILE_TYPE_MM;
2650
2651     closeFile(file);
2652   }
2653
2654   return type;
2655 }
2656
2657 static boolean checkForPackageFromBasename(char *basename)
2658 {
2659   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2660   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2661
2662   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2663 }
2664
2665 static char *getSingleLevelBasenameExt(int nr, char *extension)
2666 {
2667   static char basename[MAX_FILENAME_LEN];
2668
2669   if (nr < 0)
2670     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2671   else
2672     sprintf(basename, "%03d.%s", nr, extension);
2673
2674   return basename;
2675 }
2676
2677 static char *getSingleLevelBasename(int nr)
2678 {
2679   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2680 }
2681
2682 static char *getPackedLevelBasename(int type)
2683 {
2684   static char basename[MAX_FILENAME_LEN];
2685   char *directory = getCurrentLevelDir();
2686   Directory *dir;
2687   DirectoryEntry *dir_entry;
2688
2689   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2690
2691   if ((dir = openDirectory(directory)) == NULL)
2692   {
2693     Warn("cannot read current level directory '%s'", directory);
2694
2695     return basename;
2696   }
2697
2698   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2699   {
2700     char *entry_basename = dir_entry->basename;
2701     int entry_type = getFileTypeFromBasename(entry_basename);
2702
2703     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2704     {
2705       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2706           type == entry_type)
2707       {
2708         strcpy(basename, entry_basename);
2709
2710         break;
2711       }
2712     }
2713   }
2714
2715   closeDirectory(dir);
2716
2717   return basename;
2718 }
2719
2720 static char *getSingleLevelFilename(int nr)
2721 {
2722   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2723 }
2724
2725 #if ENABLE_UNUSED_CODE
2726 static char *getPackedLevelFilename(int type)
2727 {
2728   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2729 }
2730 #endif
2731
2732 char *getDefaultLevelFilename(int nr)
2733 {
2734   return getSingleLevelFilename(nr);
2735 }
2736
2737 #if ENABLE_UNUSED_CODE
2738 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2739                                                  int type)
2740 {
2741   lfi->type = type;
2742   lfi->packed = FALSE;
2743
2744   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2745   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2746 }
2747 #endif
2748
2749 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2750                                                  int type, char *format, ...)
2751 {
2752   static char basename[MAX_FILENAME_LEN];
2753   va_list ap;
2754
2755   va_start(ap, format);
2756   vsprintf(basename, format, ap);
2757   va_end(ap);
2758
2759   lfi->type = type;
2760   lfi->packed = FALSE;
2761
2762   setString(&lfi->basename, basename);
2763   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2764 }
2765
2766 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2767                                                  int type)
2768 {
2769   lfi->type = type;
2770   lfi->packed = TRUE;
2771
2772   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2773   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2774 }
2775
2776 static int getFiletypeFromID(char *filetype_id)
2777 {
2778   char *filetype_id_lower;
2779   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2780   int i;
2781
2782   if (filetype_id == NULL)
2783     return LEVEL_FILE_TYPE_UNKNOWN;
2784
2785   filetype_id_lower = getStringToLower(filetype_id);
2786
2787   for (i = 0; filetype_id_list[i].id != NULL; i++)
2788   {
2789     char *id_lower = getStringToLower(filetype_id_list[i].id);
2790     
2791     if (strEqual(filetype_id_lower, id_lower))
2792       filetype = filetype_id_list[i].filetype;
2793
2794     free(id_lower);
2795
2796     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2797       break;
2798   }
2799
2800   free(filetype_id_lower);
2801
2802   return filetype;
2803 }
2804
2805 char *getLocalLevelTemplateFilename(void)
2806 {
2807   return getDefaultLevelFilename(-1);
2808 }
2809
2810 char *getGlobalLevelTemplateFilename(void)
2811 {
2812   // global variable "leveldir_current" must be modified in the loop below
2813   LevelDirTree *leveldir_current_last = leveldir_current;
2814   char *filename = NULL;
2815
2816   // check for template level in path from current to topmost tree node
2817
2818   while (leveldir_current != NULL)
2819   {
2820     filename = getDefaultLevelFilename(-1);
2821
2822     if (fileExists(filename))
2823       break;
2824
2825     leveldir_current = leveldir_current->node_parent;
2826   }
2827
2828   // restore global variable "leveldir_current" modified in above loop
2829   leveldir_current = leveldir_current_last;
2830
2831   return filename;
2832 }
2833
2834 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2835 {
2836   int nr = lfi->nr;
2837
2838   // special case: level number is negative => check for level template file
2839   if (nr < 0)
2840   {
2841     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2842                                          getSingleLevelBasename(-1));
2843
2844     // replace local level template filename with global template filename
2845     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2846
2847     // no fallback if template file not existing
2848     return;
2849   }
2850
2851   // special case: check for file name/pattern specified in "levelinfo.conf"
2852   if (leveldir_current->level_filename != NULL)
2853   {
2854     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2855
2856     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2857                                          leveldir_current->level_filename, nr);
2858
2859     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2860
2861     if (fileExists(lfi->filename))
2862       return;
2863   }
2864   else if (leveldir_current->level_filetype != NULL)
2865   {
2866     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2867
2868     // check for specified native level file with standard file name
2869     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2870                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2871     if (fileExists(lfi->filename))
2872       return;
2873   }
2874
2875   // check for native Rocks'n'Diamonds level file
2876   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2877                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2878   if (fileExists(lfi->filename))
2879     return;
2880
2881   // check for native Boulder Dash level file
2882   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2883   if (fileExists(lfi->filename))
2884     return;
2885
2886   // check for Emerald Mine level file (V1)
2887   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2888                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2889   if (fileExists(lfi->filename))
2890     return;
2891   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2892                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2893   if (fileExists(lfi->filename))
2894     return;
2895
2896   // check for Emerald Mine level file (V2 to V5)
2897   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2898   if (fileExists(lfi->filename))
2899     return;
2900
2901   // check for Emerald Mine level file (V6 / single mode)
2902   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2903   if (fileExists(lfi->filename))
2904     return;
2905   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2906   if (fileExists(lfi->filename))
2907     return;
2908
2909   // check for Emerald Mine level file (V6 / teamwork mode)
2910   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2911   if (fileExists(lfi->filename))
2912     return;
2913   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2914   if (fileExists(lfi->filename))
2915     return;
2916
2917   // check for various packed level file formats
2918   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2919   if (fileExists(lfi->filename))
2920     return;
2921
2922   // no known level file found -- use default values (and fail later)
2923   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2924                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2925 }
2926
2927 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2928 {
2929   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2930     lfi->type = getFileTypeFromBasename(lfi->basename);
2931
2932   if (lfi->type == LEVEL_FILE_TYPE_RND)
2933     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2934 }
2935
2936 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2937 {
2938   // always start with reliable default values
2939   setFileInfoToDefaults(level_file_info);
2940
2941   level_file_info->nr = nr;     // set requested level number
2942
2943   determineLevelFileInfo_Filename(level_file_info);
2944   determineLevelFileInfo_Filetype(level_file_info);
2945 }
2946
2947 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2948                               struct LevelFileInfo *lfi_to)
2949 {
2950   lfi_to->nr     = lfi_from->nr;
2951   lfi_to->type   = lfi_from->type;
2952   lfi_to->packed = lfi_from->packed;
2953
2954   setString(&lfi_to->basename, lfi_from->basename);
2955   setString(&lfi_to->filename, lfi_from->filename);
2956 }
2957
2958 // ----------------------------------------------------------------------------
2959 // functions for loading R'n'D level
2960 // ----------------------------------------------------------------------------
2961
2962 int getMappedElement(int element)
2963 {
2964   // remap some (historic, now obsolete) elements
2965
2966   switch (element)
2967   {
2968     case EL_PLAYER_OBSOLETE:
2969       element = EL_PLAYER_1;
2970       break;
2971
2972     case EL_KEY_OBSOLETE:
2973       element = EL_KEY_1;
2974       break;
2975
2976     case EL_EM_KEY_1_FILE_OBSOLETE:
2977       element = EL_EM_KEY_1;
2978       break;
2979
2980     case EL_EM_KEY_2_FILE_OBSOLETE:
2981       element = EL_EM_KEY_2;
2982       break;
2983
2984     case EL_EM_KEY_3_FILE_OBSOLETE:
2985       element = EL_EM_KEY_3;
2986       break;
2987
2988     case EL_EM_KEY_4_FILE_OBSOLETE:
2989       element = EL_EM_KEY_4;
2990       break;
2991
2992     case EL_ENVELOPE_OBSOLETE:
2993       element = EL_ENVELOPE_1;
2994       break;
2995
2996     case EL_SP_EMPTY:
2997       element = EL_EMPTY;
2998       break;
2999
3000     default:
3001       if (element >= NUM_FILE_ELEMENTS)
3002       {
3003         Warn("invalid level element %d", element);
3004
3005         element = EL_UNKNOWN;
3006       }
3007       break;
3008   }
3009
3010   return element;
3011 }
3012
3013 static int getMappedElementByVersion(int element, int game_version)
3014 {
3015   // remap some elements due to certain game version
3016
3017   if (game_version <= VERSION_IDENT(2,2,0,0))
3018   {
3019     // map game font elements
3020     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3021                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3022                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3023                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3024   }
3025
3026   if (game_version < VERSION_IDENT(3,0,0,0))
3027   {
3028     // map Supaplex gravity tube elements
3029     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3030                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3031                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3032                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3033                element);
3034   }
3035
3036   return element;
3037 }
3038
3039 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3040 {
3041   level->file_version = getFileVersion(file);
3042   level->game_version = getFileVersion(file);
3043
3044   return chunk_size;
3045 }
3046
3047 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3048 {
3049   level->creation_date.year  = getFile16BitBE(file);
3050   level->creation_date.month = getFile8Bit(file);
3051   level->creation_date.day   = getFile8Bit(file);
3052
3053   level->creation_date.src   = DATE_SRC_LEVELFILE;
3054
3055   return chunk_size;
3056 }
3057
3058 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3059 {
3060   int initial_player_stepsize;
3061   int initial_player_gravity;
3062   int i, x, y;
3063
3064   level->fieldx = getFile8Bit(file);
3065   level->fieldy = getFile8Bit(file);
3066
3067   level->time           = getFile16BitBE(file);
3068   level->gems_needed    = getFile16BitBE(file);
3069
3070   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3071     level->name[i] = getFile8Bit(file);
3072   level->name[MAX_LEVEL_NAME_LEN] = 0;
3073
3074   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3075     level->score[i] = getFile8Bit(file);
3076
3077   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3078   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3079     for (y = 0; y < 3; y++)
3080       for (x = 0; x < 3; x++)
3081         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3082
3083   level->amoeba_speed           = getFile8Bit(file);
3084   level->time_magic_wall        = getFile8Bit(file);
3085   level->time_wheel             = getFile8Bit(file);
3086   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3087
3088   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3089                                    STEPSIZE_NORMAL);
3090
3091   for (i = 0; i < MAX_PLAYERS; i++)
3092     level->initial_player_stepsize[i] = initial_player_stepsize;
3093
3094   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3095
3096   for (i = 0; i < MAX_PLAYERS; i++)
3097     level->initial_player_gravity[i] = initial_player_gravity;
3098
3099   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3100   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3101
3102   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3103
3104   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3105   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3106   level->can_move_into_acid_bits = getFile32BitBE(file);
3107   level->dont_collide_with_bits = getFile8Bit(file);
3108
3109   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3110   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3111
3112   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3113   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3114   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3115
3116   level->game_engine_type       = getFile8Bit(file);
3117
3118   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3119
3120   return chunk_size;
3121 }
3122
3123 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3124 {
3125   int i;
3126
3127   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3128     level->name[i] = getFile8Bit(file);
3129   level->name[MAX_LEVEL_NAME_LEN] = 0;
3130
3131   return chunk_size;
3132 }
3133
3134 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3135 {
3136   int i;
3137
3138   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3139     level->author[i] = getFile8Bit(file);
3140   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3141
3142   return chunk_size;
3143 }
3144
3145 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3146 {
3147   int x, y;
3148   int chunk_size_expected = level->fieldx * level->fieldy;
3149
3150   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3151      stored with 16-bit encoding (and should be twice as big then).
3152      Even worse, playfield data was stored 16-bit when only yamyam content
3153      contained 16-bit elements and vice versa. */
3154
3155   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3156     chunk_size_expected *= 2;
3157
3158   if (chunk_size_expected != chunk_size)
3159   {
3160     ReadUnusedBytesFromFile(file, chunk_size);
3161     return chunk_size_expected;
3162   }
3163
3164   for (y = 0; y < level->fieldy; y++)
3165     for (x = 0; x < level->fieldx; x++)
3166       level->field[x][y] =
3167         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3168                          getFile8Bit(file));
3169   return chunk_size;
3170 }
3171
3172 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3173 {
3174   int i, x, y;
3175   int header_size = 4;
3176   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3177   int chunk_size_expected = header_size + content_size;
3178
3179   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3180      stored with 16-bit encoding (and should be twice as big then).
3181      Even worse, playfield data was stored 16-bit when only yamyam content
3182      contained 16-bit elements and vice versa. */
3183
3184   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3185     chunk_size_expected += content_size;
3186
3187   if (chunk_size_expected != chunk_size)
3188   {
3189     ReadUnusedBytesFromFile(file, chunk_size);
3190     return chunk_size_expected;
3191   }
3192
3193   getFile8Bit(file);
3194   level->num_yamyam_contents = getFile8Bit(file);
3195   getFile8Bit(file);
3196   getFile8Bit(file);
3197
3198   // correct invalid number of content fields -- should never happen
3199   if (level->num_yamyam_contents < 1 ||
3200       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3201     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3202
3203   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3204     for (y = 0; y < 3; y++)
3205       for (x = 0; x < 3; x++)
3206         level->yamyam_content[i].e[x][y] =
3207           getMappedElement(level->encoding_16bit_field ?
3208                            getFile16BitBE(file) : getFile8Bit(file));
3209   return chunk_size;
3210 }
3211
3212 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3213 {
3214   int i, x, y;
3215   int element;
3216   int num_contents;
3217   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3218
3219   element = getMappedElement(getFile16BitBE(file));
3220   num_contents = getFile8Bit(file);
3221
3222   getFile8Bit(file);    // content x size (unused)
3223   getFile8Bit(file);    // content y size (unused)
3224
3225   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3226
3227   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3228     for (y = 0; y < 3; y++)
3229       for (x = 0; x < 3; x++)
3230         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3231
3232   // correct invalid number of content fields -- should never happen
3233   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3234     num_contents = STD_ELEMENT_CONTENTS;
3235
3236   if (element == EL_YAMYAM)
3237   {
3238     level->num_yamyam_contents = num_contents;
3239
3240     for (i = 0; i < num_contents; i++)
3241       for (y = 0; y < 3; y++)
3242         for (x = 0; x < 3; x++)
3243           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3244   }
3245   else if (element == EL_BD_AMOEBA)
3246   {
3247     level->amoeba_content = content_array[0][0][0];
3248   }
3249   else
3250   {
3251     Warn("cannot load content for element '%d'", element);
3252   }
3253
3254   return chunk_size;
3255 }
3256
3257 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3258 {
3259   int i;
3260   int element;
3261   int envelope_nr;
3262   int envelope_len;
3263   int chunk_size_expected;
3264
3265   element = getMappedElement(getFile16BitBE(file));
3266   if (!IS_ENVELOPE(element))
3267     element = EL_ENVELOPE_1;
3268
3269   envelope_nr = element - EL_ENVELOPE_1;
3270
3271   envelope_len = getFile16BitBE(file);
3272
3273   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3274   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3275
3276   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3277
3278   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3279   if (chunk_size_expected != chunk_size)
3280   {
3281     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3282     return chunk_size_expected;
3283   }
3284
3285   for (i = 0; i < envelope_len; i++)
3286     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3287
3288   return chunk_size;
3289 }
3290
3291 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3292 {
3293   int num_changed_custom_elements = getFile16BitBE(file);
3294   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3295   int i;
3296
3297   if (chunk_size_expected != chunk_size)
3298   {
3299     ReadUnusedBytesFromFile(file, chunk_size - 2);
3300     return chunk_size_expected;
3301   }
3302
3303   for (i = 0; i < num_changed_custom_elements; i++)
3304   {
3305     int element = getMappedElement(getFile16BitBE(file));
3306     int properties = getFile32BitBE(file);
3307
3308     if (IS_CUSTOM_ELEMENT(element))
3309       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3310     else
3311       Warn("invalid custom element number %d", element);
3312
3313     // older game versions that wrote level files with CUS1 chunks used
3314     // different default push delay values (not yet stored in level file)
3315     element_info[element].push_delay_fixed = 2;
3316     element_info[element].push_delay_random = 8;
3317   }
3318
3319   level->file_has_custom_elements = TRUE;
3320
3321   return chunk_size;
3322 }
3323
3324 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3325 {
3326   int num_changed_custom_elements = getFile16BitBE(file);
3327   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3328   int i;
3329
3330   if (chunk_size_expected != chunk_size)
3331   {
3332     ReadUnusedBytesFromFile(file, chunk_size - 2);
3333     return chunk_size_expected;
3334   }
3335
3336   for (i = 0; i < num_changed_custom_elements; i++)
3337   {
3338     int element = getMappedElement(getFile16BitBE(file));
3339     int custom_target_element = getMappedElement(getFile16BitBE(file));
3340
3341     if (IS_CUSTOM_ELEMENT(element))
3342       element_info[element].change->target_element = custom_target_element;
3343     else
3344       Warn("invalid custom element number %d", element);
3345   }
3346
3347   level->file_has_custom_elements = TRUE;
3348
3349   return chunk_size;
3350 }
3351
3352 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3353 {
3354   int num_changed_custom_elements = getFile16BitBE(file);
3355   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3356   int i, j, x, y;
3357
3358   if (chunk_size_expected != chunk_size)
3359   {
3360     ReadUnusedBytesFromFile(file, chunk_size - 2);
3361     return chunk_size_expected;
3362   }
3363
3364   for (i = 0; i < num_changed_custom_elements; i++)
3365   {
3366     int element = getMappedElement(getFile16BitBE(file));
3367     struct ElementInfo *ei = &element_info[element];
3368     unsigned int event_bits;
3369
3370     if (!IS_CUSTOM_ELEMENT(element))
3371     {
3372       Warn("invalid custom element number %d", element);
3373
3374       element = EL_INTERNAL_DUMMY;
3375     }
3376
3377     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3378       ei->description[j] = getFile8Bit(file);
3379     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3380
3381     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3382
3383     // some free bytes for future properties and padding
3384     ReadUnusedBytesFromFile(file, 7);
3385
3386     ei->use_gfx_element = getFile8Bit(file);
3387     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3388
3389     ei->collect_score_initial = getFile8Bit(file);
3390     ei->collect_count_initial = getFile8Bit(file);
3391
3392     ei->push_delay_fixed = getFile16BitBE(file);
3393     ei->push_delay_random = getFile16BitBE(file);
3394     ei->move_delay_fixed = getFile16BitBE(file);
3395     ei->move_delay_random = getFile16BitBE(file);
3396
3397     ei->move_pattern = getFile16BitBE(file);
3398     ei->move_direction_initial = getFile8Bit(file);
3399     ei->move_stepsize = getFile8Bit(file);
3400
3401     for (y = 0; y < 3; y++)
3402       for (x = 0; x < 3; x++)
3403         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3404
3405     // bits 0 - 31 of "has_event[]"
3406     event_bits = getFile32BitBE(file);
3407     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3408       if (event_bits & (1u << j))
3409         ei->change->has_event[j] = TRUE;
3410
3411     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3412
3413     ei->change->delay_fixed = getFile16BitBE(file);
3414     ei->change->delay_random = getFile16BitBE(file);
3415     ei->change->delay_frames = getFile16BitBE(file);
3416
3417     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3418
3419     ei->change->explode = getFile8Bit(file);
3420     ei->change->use_target_content = getFile8Bit(file);
3421     ei->change->only_if_complete = getFile8Bit(file);
3422     ei->change->use_random_replace = getFile8Bit(file);
3423
3424     ei->change->random_percentage = getFile8Bit(file);
3425     ei->change->replace_when = getFile8Bit(file);
3426
3427     for (y = 0; y < 3; y++)
3428       for (x = 0; x < 3; x++)
3429         ei->change->target_content.e[x][y] =
3430           getMappedElement(getFile16BitBE(file));
3431
3432     ei->slippery_type = getFile8Bit(file);
3433
3434     // some free bytes for future properties and padding
3435     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3436
3437     // mark that this custom element has been modified
3438     ei->modified_settings = TRUE;
3439   }
3440
3441   level->file_has_custom_elements = TRUE;
3442
3443   return chunk_size;
3444 }
3445
3446 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3447 {
3448   struct ElementInfo *ei;
3449   int chunk_size_expected;
3450   int element;
3451   int i, j, x, y;
3452
3453   // ---------- custom element base property values (96 bytes) ----------------
3454
3455   element = getMappedElement(getFile16BitBE(file));
3456
3457   if (!IS_CUSTOM_ELEMENT(element))
3458   {
3459     Warn("invalid custom element number %d", element);
3460
3461     ReadUnusedBytesFromFile(file, chunk_size - 2);
3462
3463     return chunk_size;
3464   }
3465
3466   ei = &element_info[element];
3467
3468   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3469     ei->description[i] = getFile8Bit(file);
3470   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3471
3472   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3473
3474   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3475
3476   ei->num_change_pages = getFile8Bit(file);
3477
3478   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3479   if (chunk_size_expected != chunk_size)
3480   {
3481     ReadUnusedBytesFromFile(file, chunk_size - 43);
3482     return chunk_size_expected;
3483   }
3484
3485   ei->ce_value_fixed_initial = getFile16BitBE(file);
3486   ei->ce_value_random_initial = getFile16BitBE(file);
3487   ei->use_last_ce_value = getFile8Bit(file);
3488
3489   ei->use_gfx_element = getFile8Bit(file);
3490   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3491
3492   ei->collect_score_initial = getFile8Bit(file);
3493   ei->collect_count_initial = getFile8Bit(file);
3494
3495   ei->drop_delay_fixed = getFile8Bit(file);
3496   ei->push_delay_fixed = getFile8Bit(file);
3497   ei->drop_delay_random = getFile8Bit(file);
3498   ei->push_delay_random = getFile8Bit(file);
3499   ei->move_delay_fixed = getFile16BitBE(file);
3500   ei->move_delay_random = getFile16BitBE(file);
3501
3502   // bits 0 - 15 of "move_pattern" ...
3503   ei->move_pattern = getFile16BitBE(file);
3504   ei->move_direction_initial = getFile8Bit(file);
3505   ei->move_stepsize = getFile8Bit(file);
3506
3507   ei->slippery_type = getFile8Bit(file);
3508
3509   for (y = 0; y < 3; y++)
3510     for (x = 0; x < 3; x++)
3511       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3512
3513   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3514   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3515   ei->move_leave_type = getFile8Bit(file);
3516
3517   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3518   ei->move_pattern |= (getFile16BitBE(file) << 16);
3519
3520   ei->access_direction = getFile8Bit(file);
3521
3522   ei->explosion_delay = getFile8Bit(file);
3523   ei->ignition_delay = getFile8Bit(file);
3524   ei->explosion_type = getFile8Bit(file);
3525
3526   // some free bytes for future custom property values and padding
3527   ReadUnusedBytesFromFile(file, 1);
3528
3529   // ---------- change page property values (48 bytes) ------------------------
3530
3531   setElementChangePages(ei, ei->num_change_pages);
3532
3533   for (i = 0; i < ei->num_change_pages; i++)
3534   {
3535     struct ElementChangeInfo *change = &ei->change_page[i];
3536     unsigned int event_bits;
3537
3538     // always start with reliable default values
3539     setElementChangeInfoToDefaults(change);
3540
3541     // bits 0 - 31 of "has_event[]" ...
3542     event_bits = getFile32BitBE(file);
3543     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3544       if (event_bits & (1u << j))
3545         change->has_event[j] = TRUE;
3546
3547     change->target_element = getMappedElement(getFile16BitBE(file));
3548
3549     change->delay_fixed = getFile16BitBE(file);
3550     change->delay_random = getFile16BitBE(file);
3551     change->delay_frames = getFile16BitBE(file);
3552
3553     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3554
3555     change->explode = getFile8Bit(file);
3556     change->use_target_content = getFile8Bit(file);
3557     change->only_if_complete = getFile8Bit(file);
3558     change->use_random_replace = getFile8Bit(file);
3559
3560     change->random_percentage = getFile8Bit(file);
3561     change->replace_when = getFile8Bit(file);
3562
3563     for (y = 0; y < 3; y++)
3564       for (x = 0; x < 3; x++)
3565         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3566
3567     change->can_change = getFile8Bit(file);
3568
3569     change->trigger_side = getFile8Bit(file);
3570
3571     change->trigger_player = getFile8Bit(file);
3572     change->trigger_page = getFile8Bit(file);
3573
3574     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3575                             CH_PAGE_ANY : (1 << change->trigger_page));
3576
3577     change->has_action = getFile8Bit(file);
3578     change->action_type = getFile8Bit(file);
3579     change->action_mode = getFile8Bit(file);
3580     change->action_arg = getFile16BitBE(file);
3581
3582     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3583     event_bits = getFile8Bit(file);
3584     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3585       if (event_bits & (1u << (j - 32)))
3586         change->has_event[j] = TRUE;
3587   }
3588
3589   // mark this custom element as modified
3590   ei->modified_settings = TRUE;
3591
3592   level->file_has_custom_elements = TRUE;
3593
3594   return chunk_size;
3595 }
3596
3597 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3598 {
3599   struct ElementInfo *ei;
3600   struct ElementGroupInfo *group;
3601   int element;
3602   int i;
3603
3604   element = getMappedElement(getFile16BitBE(file));
3605
3606   if (!IS_GROUP_ELEMENT(element))
3607   {
3608     Warn("invalid group element number %d", element);
3609
3610     ReadUnusedBytesFromFile(file, chunk_size - 2);
3611
3612     return chunk_size;
3613   }
3614
3615   ei = &element_info[element];
3616
3617   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3618     ei->description[i] = getFile8Bit(file);
3619   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3620
3621   group = element_info[element].group;
3622
3623   group->num_elements = getFile8Bit(file);
3624
3625   ei->use_gfx_element = getFile8Bit(file);
3626   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3627
3628   group->choice_mode = getFile8Bit(file);
3629
3630   // some free bytes for future values and padding
3631   ReadUnusedBytesFromFile(file, 3);
3632
3633   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3634     group->element[i] = getMappedElement(getFile16BitBE(file));
3635
3636   // mark this group element as modified
3637   element_info[element].modified_settings = TRUE;
3638
3639   level->file_has_custom_elements = TRUE;
3640
3641   return chunk_size;
3642 }
3643
3644 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3645                                 int element, int real_element)
3646 {
3647   int micro_chunk_size = 0;
3648   int conf_type = getFile8Bit(file);
3649   int byte_mask = conf_type & CONF_MASK_BYTES;
3650   boolean element_found = FALSE;
3651   int i;
3652
3653   micro_chunk_size += 1;
3654
3655   if (byte_mask == CONF_MASK_MULTI_BYTES)
3656   {
3657     int num_bytes = getFile16BitBE(file);
3658     byte *buffer = checked_malloc(num_bytes);
3659
3660     ReadBytesFromFile(file, buffer, num_bytes);
3661
3662     for (i = 0; conf[i].data_type != -1; i++)
3663     {
3664       if (conf[i].element == element &&
3665           conf[i].conf_type == conf_type)
3666       {
3667         int data_type = conf[i].data_type;
3668         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3669         int max_num_entities = conf[i].max_num_entities;
3670
3671         if (num_entities > max_num_entities)
3672         {
3673           Warn("truncating number of entities for element %d from %d to %d",
3674                element, num_entities, max_num_entities);
3675
3676           num_entities = max_num_entities;
3677         }
3678
3679         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3680                                   data_type == TYPE_CONTENT_LIST))
3681         {
3682           // for element and content lists, zero entities are not allowed
3683           Warn("found empty list of entities for element %d", element);
3684
3685           // do not set "num_entities" here to prevent reading behind buffer
3686
3687           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3688         }
3689         else
3690         {
3691           *(int *)(conf[i].num_entities) = num_entities;
3692         }
3693
3694         element_found = TRUE;
3695
3696         if (data_type == TYPE_STRING)
3697         {
3698           char *string = (char *)(conf[i].value);
3699           int j;
3700
3701           for (j = 0; j < max_num_entities; j++)
3702             string[j] = (j < num_entities ? buffer[j] : '\0');
3703         }
3704         else if (data_type == TYPE_ELEMENT_LIST)
3705         {
3706           int *element_array = (int *)(conf[i].value);
3707           int j;
3708
3709           for (j = 0; j < num_entities; j++)
3710             element_array[j] =
3711               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3712         }
3713         else if (data_type == TYPE_CONTENT_LIST)
3714         {
3715           struct Content *content= (struct Content *)(conf[i].value);
3716           int c, x, y;
3717
3718           for (c = 0; c < num_entities; c++)
3719             for (y = 0; y < 3; y++)
3720               for (x = 0; x < 3; x++)
3721                 content[c].e[x][y] =
3722                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3723         }
3724         else
3725           element_found = FALSE;
3726
3727         break;
3728       }
3729     }
3730
3731     checked_free(buffer);
3732
3733     micro_chunk_size += 2 + num_bytes;
3734   }
3735   else          // constant size configuration data (1, 2 or 4 bytes)
3736   {
3737     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3738                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3739                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3740
3741     for (i = 0; conf[i].data_type != -1; i++)
3742     {
3743       if (conf[i].element == element &&
3744           conf[i].conf_type == conf_type)
3745       {
3746         int data_type = conf[i].data_type;
3747
3748         if (data_type == TYPE_ELEMENT)
3749           value = getMappedElement(value);
3750
3751         if (data_type == TYPE_BOOLEAN)
3752           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3753         else
3754           *(int *)    (conf[i].value) = value;
3755
3756         element_found = TRUE;
3757
3758         break;
3759       }
3760     }
3761
3762     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3763   }
3764
3765   if (!element_found)
3766   {
3767     char *error_conf_chunk_bytes =
3768       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3769        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3770        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3771     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3772     int error_element = real_element;
3773
3774     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3775          error_conf_chunk_bytes, error_conf_chunk_token,
3776          error_element, EL_NAME(error_element));
3777   }
3778
3779   return micro_chunk_size;
3780 }
3781
3782 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3783 {
3784   int real_chunk_size = 0;
3785
3786   li = *level;          // copy level data into temporary buffer
3787
3788   while (!checkEndOfFile(file))
3789   {
3790     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3791
3792     if (real_chunk_size >= chunk_size)
3793       break;
3794   }
3795
3796   *level = li;          // copy temporary buffer back to level data
3797
3798   return real_chunk_size;
3799 }
3800
3801 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3802 {
3803   int real_chunk_size = 0;
3804
3805   li = *level;          // copy level data into temporary buffer
3806
3807   while (!checkEndOfFile(file))
3808   {
3809     int element = getMappedElement(getFile16BitBE(file));
3810
3811     real_chunk_size += 2;
3812     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3813                                             element, element);
3814     if (real_chunk_size >= chunk_size)
3815       break;
3816   }
3817
3818   *level = li;          // copy temporary buffer back to level data
3819
3820   return real_chunk_size;
3821 }
3822
3823 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3824 {
3825   int real_chunk_size = 0;
3826
3827   li = *level;          // copy level data into temporary buffer
3828
3829   while (!checkEndOfFile(file))
3830   {
3831     int element = getMappedElement(getFile16BitBE(file));
3832
3833     real_chunk_size += 2;
3834     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3835                                             element, element);
3836     if (real_chunk_size >= chunk_size)
3837       break;
3838   }
3839
3840   *level = li;          // copy temporary buffer back to level data
3841
3842   return real_chunk_size;
3843 }
3844
3845 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3846 {
3847   int element = getMappedElement(getFile16BitBE(file));
3848   int envelope_nr = element - EL_ENVELOPE_1;
3849   int real_chunk_size = 2;
3850
3851   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3852
3853   while (!checkEndOfFile(file))
3854   {
3855     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3856                                             -1, element);
3857
3858     if (real_chunk_size >= chunk_size)
3859       break;
3860   }
3861
3862   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3863
3864   return real_chunk_size;
3865 }
3866
3867 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3868 {
3869   int element = getMappedElement(getFile16BitBE(file));
3870   int real_chunk_size = 2;
3871   struct ElementInfo *ei = &element_info[element];
3872   int i;
3873
3874   xx_ei = *ei;          // copy element data into temporary buffer
3875
3876   xx_ei.num_change_pages = -1;
3877
3878   while (!checkEndOfFile(file))
3879   {
3880     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3881                                             -1, element);
3882     if (xx_ei.num_change_pages != -1)
3883       break;
3884
3885     if (real_chunk_size >= chunk_size)
3886       break;
3887   }
3888
3889   *ei = xx_ei;
3890
3891   if (ei->num_change_pages == -1)
3892   {
3893     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3894          EL_NAME(element));
3895
3896     ei->num_change_pages = 1;
3897
3898     setElementChangePages(ei, 1);
3899     setElementChangeInfoToDefaults(ei->change);
3900
3901     return real_chunk_size;
3902   }
3903
3904   // initialize number of change pages stored for this custom element
3905   setElementChangePages(ei, ei->num_change_pages);
3906   for (i = 0; i < ei->num_change_pages; i++)
3907     setElementChangeInfoToDefaults(&ei->change_page[i]);
3908
3909   // start with reading properties for the first change page
3910   xx_current_change_page = 0;
3911
3912   while (!checkEndOfFile(file))
3913   {
3914     // level file might contain invalid change page number
3915     if (xx_current_change_page >= ei->num_change_pages)
3916       break;
3917
3918     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3919
3920     xx_change = *change;        // copy change data into temporary buffer
3921
3922     resetEventBits();           // reset bits; change page might have changed
3923
3924     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3925                                             -1, element);
3926
3927     *change = xx_change;
3928
3929     setEventFlagsFromEventBits(change);
3930
3931     if (real_chunk_size >= chunk_size)
3932       break;
3933   }
3934
3935   level->file_has_custom_elements = TRUE;
3936
3937   return real_chunk_size;
3938 }
3939
3940 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3941 {
3942   int element = getMappedElement(getFile16BitBE(file));
3943   int real_chunk_size = 2;
3944   struct ElementInfo *ei = &element_info[element];
3945   struct ElementGroupInfo *group = ei->group;
3946
3947   if (group == NULL)
3948     return -1;
3949
3950   xx_ei = *ei;          // copy element data into temporary buffer
3951   xx_group = *group;    // copy group data into temporary buffer
3952
3953   while (!checkEndOfFile(file))
3954   {
3955     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3956                                             -1, element);
3957
3958     if (real_chunk_size >= chunk_size)
3959       break;
3960   }
3961
3962   *ei = xx_ei;
3963   *group = xx_group;
3964
3965   level->file_has_custom_elements = TRUE;
3966
3967   return real_chunk_size;
3968 }
3969
3970 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3971 {
3972   int element = getMappedElement(getFile16BitBE(file));
3973   int real_chunk_size = 2;
3974   struct ElementInfo *ei = &element_info[element];
3975
3976   xx_ei = *ei;          // copy element data into temporary buffer
3977
3978   while (!checkEndOfFile(file))
3979   {
3980     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3981                                             -1, element);
3982
3983     if (real_chunk_size >= chunk_size)
3984       break;
3985   }
3986
3987   *ei = xx_ei;
3988
3989   level->file_has_custom_elements = TRUE;
3990
3991   return real_chunk_size;
3992 }
3993
3994 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3995                                       struct LevelFileInfo *level_file_info,
3996                                       boolean level_info_only)
3997 {
3998   char *filename = level_file_info->filename;
3999   char cookie[MAX_LINE_LEN];
4000   char chunk_name[CHUNK_ID_LEN + 1];
4001   int chunk_size;
4002   File *file;
4003
4004   if (!(file = openFile(filename, MODE_READ)))
4005   {
4006     level->no_valid_file = TRUE;
4007     level->no_level_file = TRUE;
4008
4009     if (level_info_only)
4010       return;
4011
4012     Warn("cannot read level '%s' -- using empty level", filename);
4013
4014     if (!setup.editor.use_template_for_new_levels)
4015       return;
4016
4017     // if level file not found, try to initialize level data from template
4018     filename = getGlobalLevelTemplateFilename();
4019
4020     if (!(file = openFile(filename, MODE_READ)))
4021       return;
4022
4023     // default: for empty levels, use level template for custom elements
4024     level->use_custom_template = TRUE;
4025
4026     level->no_valid_file = FALSE;
4027   }
4028
4029   getFileChunkBE(file, chunk_name, NULL);
4030   if (strEqual(chunk_name, "RND1"))
4031   {
4032     getFile32BitBE(file);               // not used
4033
4034     getFileChunkBE(file, chunk_name, NULL);
4035     if (!strEqual(chunk_name, "CAVE"))
4036     {
4037       level->no_valid_file = TRUE;
4038
4039       Warn("unknown format of level file '%s'", filename);
4040
4041       closeFile(file);
4042
4043       return;
4044     }
4045   }
4046   else  // check for pre-2.0 file format with cookie string
4047   {
4048     strcpy(cookie, chunk_name);
4049     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4050       cookie[4] = '\0';
4051     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4052       cookie[strlen(cookie) - 1] = '\0';
4053
4054     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4055     {
4056       level->no_valid_file = TRUE;
4057
4058       Warn("unknown format of level file '%s'", filename);
4059
4060       closeFile(file);
4061
4062       return;
4063     }
4064
4065     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4066     {
4067       level->no_valid_file = TRUE;
4068
4069       Warn("unsupported version of level file '%s'", filename);
4070
4071       closeFile(file);
4072
4073       return;
4074     }
4075
4076     // pre-2.0 level files have no game version, so use file version here
4077     level->game_version = level->file_version;
4078   }
4079
4080   if (level->file_version < FILE_VERSION_1_2)
4081   {
4082     // level files from versions before 1.2.0 without chunk structure
4083     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4084     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4085   }
4086   else
4087   {
4088     static struct
4089     {
4090       char *name;
4091       int size;
4092       int (*loader)(File *, int, struct LevelInfo *);
4093     }
4094     chunk_info[] =
4095     {
4096       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4097       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4098       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4099       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4100       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4101       { "INFO", -1,                     LoadLevel_INFO },
4102       { "BODY", -1,                     LoadLevel_BODY },
4103       { "CONT", -1,                     LoadLevel_CONT },
4104       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4105       { "CNT3", -1,                     LoadLevel_CNT3 },
4106       { "CUS1", -1,                     LoadLevel_CUS1 },
4107       { "CUS2", -1,                     LoadLevel_CUS2 },
4108       { "CUS3", -1,                     LoadLevel_CUS3 },
4109       { "CUS4", -1,                     LoadLevel_CUS4 },
4110       { "GRP1", -1,                     LoadLevel_GRP1 },
4111       { "CONF", -1,                     LoadLevel_CONF },
4112       { "ELEM", -1,                     LoadLevel_ELEM },
4113       { "NOTE", -1,                     LoadLevel_NOTE },
4114       { "CUSX", -1,                     LoadLevel_CUSX },
4115       { "GRPX", -1,                     LoadLevel_GRPX },
4116       { "EMPX", -1,                     LoadLevel_EMPX },
4117
4118       {  NULL,  0,                      NULL }
4119     };
4120
4121     while (getFileChunkBE(file, chunk_name, &chunk_size))
4122     {
4123       int i = 0;
4124
4125       while (chunk_info[i].name != NULL &&
4126              !strEqual(chunk_name, chunk_info[i].name))
4127         i++;
4128
4129       if (chunk_info[i].name == NULL)
4130       {
4131         Warn("unknown chunk '%s' in level file '%s'",
4132              chunk_name, filename);
4133
4134         ReadUnusedBytesFromFile(file, chunk_size);
4135       }
4136       else if (chunk_info[i].size != -1 &&
4137                chunk_info[i].size != chunk_size)
4138       {
4139         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4140              chunk_size, chunk_name, filename);
4141
4142         ReadUnusedBytesFromFile(file, chunk_size);
4143       }
4144       else
4145       {
4146         // call function to load this level chunk
4147         int chunk_size_expected =
4148           (chunk_info[i].loader)(file, chunk_size, level);
4149
4150         if (chunk_size_expected < 0)
4151         {
4152           Warn("error reading chunk '%s' in level file '%s'",
4153                chunk_name, filename);
4154
4155           break;
4156         }
4157
4158         // the size of some chunks cannot be checked before reading other
4159         // chunks first (like "HEAD" and "BODY") that contain some header
4160         // information, so check them here
4161         if (chunk_size_expected != chunk_size)
4162         {
4163           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4164                chunk_size, chunk_name, filename);
4165
4166           break;
4167         }
4168       }
4169     }
4170   }
4171
4172   closeFile(file);
4173 }
4174
4175
4176 // ----------------------------------------------------------------------------
4177 // functions for loading BD level
4178 // ----------------------------------------------------------------------------
4179
4180 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4181 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4182
4183 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4184 {
4185   struct LevelInfo_BD *level_bd = level->native_bd_level;
4186   GdCave *cave = NULL;  // will be changed below
4187   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4188   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4189   int x, y;
4190
4191   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4192
4193   // cave and map newly allocated when set to defaults above
4194   cave = level_bd->cave;
4195
4196   // level type
4197   cave->intermission                    = level->bd_intermission;
4198
4199   // level settings
4200   cave->level_time[0]                   = level->time;
4201   cave->level_diamonds[0]               = level->gems_needed;
4202
4203   // game timing
4204   cave->scheduling                      = level->bd_scheduling_type;
4205   cave->pal_timing                      = level->bd_pal_timing;
4206   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4207   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4208   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4209   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4210
4211   // scores
4212   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4213   cave->diamond_value                   = level->score[SC_EMERALD];
4214   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4215
4216   // compatibility settings
4217   cave->lineshift                       = level->bd_line_shifting_borders;
4218   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4219   cave->short_explosions                = level->bd_short_explosions;
4220
4221   // player properties
4222   cave->diagonal_movements              = level->bd_diagonal_movements;
4223   cave->active_is_first_found           = level->bd_topmost_player_active;
4224   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4225   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4226   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4227   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4228
4229   // element properties
4230   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4231   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4232   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4233   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4234   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4235   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4236   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4237   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4238   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4239
4240   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4241   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4242   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4243   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4244   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4245   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4246   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4247
4248   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4249   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4250   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4251   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4252   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4253   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4254   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4255   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4256   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4257   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4258   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4259
4260   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4261   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4262   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4263   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4264   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4265   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4266
4267   cave->slime_predictable               = level->bd_slime_is_predictable;
4268   cave->slime_correct_random            = level->bd_slime_correct_random;
4269   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4270   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4271   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4272   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4273   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4274   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4275   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4276   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4277   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4278   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4279
4280   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4281   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4282   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4283
4284   cave->biter_delay_frame               = level->bd_biter_move_delay;
4285   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4286
4287   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4288
4289   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4290
4291   cave->replicators_active              = level->bd_replicators_active;
4292   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4293
4294   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4295   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4296
4297   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4298
4299   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4300
4301   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4302   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4303   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4304
4305   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4306   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4307
4308   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4309   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4310
4311   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4312   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4313   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4314
4315   cave->gravity                         = level->bd_gravity_direction;
4316   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4317   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4318   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4319
4320   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4321   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4322   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4323   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4324
4325   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4326   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4327   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4328   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4329   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4330   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4331
4332   // level name
4333   strncpy(cave->name, level->name, sizeof(GdString));
4334   cave->name[sizeof(GdString) - 1] = '\0';
4335
4336   // playfield elements
4337   for (x = 0; x < cave->w; x++)
4338     for (y = 0; y < cave->h; y++)
4339       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4340 }
4341
4342 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4343 {
4344   struct LevelInfo_BD *level_bd = level->native_bd_level;
4345   GdCave *cave = level_bd->cave;
4346   int bd_level_nr = level_bd->level_nr;
4347   int x, y;
4348
4349   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4350   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4351
4352   // level type
4353   level->bd_intermission                = cave->intermission;
4354
4355   // level settings
4356   level->time                           = cave->level_time[bd_level_nr];
4357   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4358
4359   // game timing
4360   level->bd_scheduling_type             = cave->scheduling;
4361   level->bd_pal_timing                  = cave->pal_timing;
4362   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4363   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4364   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4365   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4366
4367   // scores
4368   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4369   level->score[SC_EMERALD]              = cave->diamond_value;
4370   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4371
4372   // compatibility settings
4373   level->bd_line_shifting_borders       = cave->lineshift;
4374   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4375   level->bd_short_explosions            = cave->short_explosions;
4376
4377   // player properties
4378   level->bd_diagonal_movements          = cave->diagonal_movements;
4379   level->bd_topmost_player_active       = cave->active_is_first_found;
4380   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4381   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4382   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4383   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4384
4385   // element properties
4386   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4387   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4388   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4389   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4390   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4391   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4392   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4393   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4394   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4395
4396   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4397   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4398   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4399   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4400   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4401   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4402   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4403
4404   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4405   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4406   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4407   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4408   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4409   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4410   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4411   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4412   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4413   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4414   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4415
4416   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4417   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4418   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4419   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4420   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4421   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4422
4423   level->bd_slime_is_predictable        = cave->slime_predictable;
4424   level->bd_slime_correct_random        = cave->slime_correct_random;
4425   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4426   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4427   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4428   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4429   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4430   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4431   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4432   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4433   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4434   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4435
4436   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4437   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4438   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4439
4440   level->bd_biter_move_delay            = cave->biter_delay_frame;
4441   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4442
4443   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4444
4445   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4446
4447   level->bd_replicators_active          = cave->replicators_active;
4448   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4449
4450   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4451   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4452
4453   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4454
4455   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4456
4457   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4458   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4459   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4460
4461   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4462   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4463
4464   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4465   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4466
4467   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4468   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4469   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4470
4471   level->bd_gravity_direction           = cave->gravity;
4472   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4473   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4474   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4475
4476   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4477   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4478   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4479   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4480
4481   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4482   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4483   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4484   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4485   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4486   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4487
4488   // level name
4489   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4490
4491   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4492   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4493
4494   // playfield elements
4495   for (x = 0; x < level->fieldx; x++)
4496     for (y = 0; y < level->fieldy; y++)
4497       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4498
4499   checked_free(cave_name);
4500 }
4501
4502 static void setTapeInfoToDefaults(void);
4503
4504 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4505 {
4506   struct LevelInfo_BD *level_bd = level->native_bd_level;
4507   GdCave *cave = level_bd->cave;
4508   GdReplay *replay = level_bd->replay;
4509   int i;
4510
4511   if (replay == NULL)
4512     return;
4513
4514   // always start with reliable default values
4515   setTapeInfoToDefaults();
4516
4517   tape.level_nr = level_nr;             // (currently not used)
4518   tape.random_seed = replay->seed;
4519
4520   TapeSetDateFromIsoDateString(replay->date);
4521
4522   tape.counter = 0;
4523   tape.pos[tape.counter].delay = 0;
4524
4525   tape.bd_replay = TRUE;
4526
4527   // all time calculations only used to display approximate tape time
4528   int cave_speed = cave->speed;
4529   int milliseconds_game = 0;
4530   int milliseconds_elapsed = 20;
4531
4532   for (i = 0; i < replay->movements->len; i++)
4533   {
4534     int replay_action = replay->movements->data[i];
4535     int tape_action = map_action_BD_to_RND(replay_action);
4536     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4537     boolean success = 0;
4538
4539     while (1)
4540     {
4541       success = TapeAddAction(action);
4542
4543       milliseconds_game += milliseconds_elapsed;
4544
4545       if (milliseconds_game >= cave_speed)
4546       {
4547         milliseconds_game -= cave_speed;
4548
4549         break;
4550       }
4551     }
4552
4553     tape.counter++;
4554     tape.pos[tape.counter].delay = 0;
4555     tape.pos[tape.counter].action[0] = 0;
4556
4557     if (!success)
4558     {
4559       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4560
4561       break;
4562     }
4563   }
4564
4565   TapeHaltRecording();
4566 }
4567
4568
4569 // ----------------------------------------------------------------------------
4570 // functions for loading EM level
4571 // ----------------------------------------------------------------------------
4572
4573 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4574 {
4575   static int ball_xy[8][2] =
4576   {
4577     { 0, 0 },
4578     { 1, 0 },
4579     { 2, 0 },
4580     { 0, 1 },
4581     { 2, 1 },
4582     { 0, 2 },
4583     { 1, 2 },
4584     { 2, 2 },
4585   };
4586   struct LevelInfo_EM *level_em = level->native_em_level;
4587   struct CAVE *cav = level_em->cav;
4588   int i, j, x, y;
4589
4590   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4591   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4592
4593   cav->time_seconds     = level->time;
4594   cav->gems_needed      = level->gems_needed;
4595
4596   cav->emerald_score    = level->score[SC_EMERALD];
4597   cav->diamond_score    = level->score[SC_DIAMOND];
4598   cav->alien_score      = level->score[SC_ROBOT];
4599   cav->tank_score       = level->score[SC_SPACESHIP];
4600   cav->bug_score        = level->score[SC_BUG];
4601   cav->eater_score      = level->score[SC_YAMYAM];
4602   cav->nut_score        = level->score[SC_NUT];
4603   cav->dynamite_score   = level->score[SC_DYNAMITE];
4604   cav->key_score        = level->score[SC_KEY];
4605   cav->exit_score       = level->score[SC_TIME_BONUS];
4606
4607   cav->num_eater_arrays = level->num_yamyam_contents;
4608
4609   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4610     for (y = 0; y < 3; y++)
4611       for (x = 0; x < 3; x++)
4612         cav->eater_array[i][y * 3 + x] =
4613           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4614
4615   cav->amoeba_time              = level->amoeba_speed;
4616   cav->wonderwall_time          = level->time_magic_wall;
4617   cav->wheel_time               = level->time_wheel;
4618
4619   cav->android_move_time        = level->android_move_time;
4620   cav->android_clone_time       = level->android_clone_time;
4621   cav->ball_random              = level->ball_random;
4622   cav->ball_active              = level->ball_active_initial;
4623   cav->ball_time                = level->ball_time;
4624   cav->num_ball_arrays          = level->num_ball_contents;
4625
4626   cav->lenses_score             = level->lenses_score;
4627   cav->magnify_score            = level->magnify_score;
4628   cav->slurp_score              = level->slurp_score;
4629
4630   cav->lenses_time              = level->lenses_time;
4631   cav->magnify_time             = level->magnify_time;
4632
4633   cav->wind_time = 9999;
4634   cav->wind_direction =
4635     map_direction_RND_to_EM(level->wind_direction_initial);
4636
4637   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4638     for (j = 0; j < 8; j++)
4639       cav->ball_array[i][j] =
4640         map_element_RND_to_EM_cave(level->ball_content[i].
4641                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4642
4643   map_android_clone_elements_RND_to_EM(level);
4644
4645   // first fill the complete playfield with the empty space element
4646   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4647     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4648       cav->cave[x][y] = Cblank;
4649
4650   // then copy the real level contents from level file into the playfield
4651   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4652   {
4653     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4654
4655     if (level->field[x][y] == EL_AMOEBA_DEAD)
4656       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4657
4658     cav->cave[x][y] = new_element;
4659   }
4660
4661   for (i = 0; i < MAX_PLAYERS; i++)
4662   {
4663     cav->player_x[i] = -1;
4664     cav->player_y[i] = -1;
4665   }
4666
4667   // initialize player positions and delete players from the playfield
4668   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4669   {
4670     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4671     {
4672       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4673
4674       cav->player_x[player_nr] = x;
4675       cav->player_y[player_nr] = y;
4676
4677       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4678     }
4679   }
4680 }
4681
4682 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4683 {
4684   static int ball_xy[8][2] =
4685   {
4686     { 0, 0 },
4687     { 1, 0 },
4688     { 2, 0 },
4689     { 0, 1 },
4690     { 2, 1 },
4691     { 0, 2 },
4692     { 1, 2 },
4693     { 2, 2 },
4694   };
4695   struct LevelInfo_EM *level_em = level->native_em_level;
4696   struct CAVE *cav = level_em->cav;
4697   int i, j, x, y;
4698
4699   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4700   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4701
4702   level->time        = cav->time_seconds;
4703   level->gems_needed = cav->gems_needed;
4704
4705   sprintf(level->name, "Level %d", level->file_info.nr);
4706
4707   level->score[SC_EMERALD]      = cav->emerald_score;
4708   level->score[SC_DIAMOND]      = cav->diamond_score;
4709   level->score[SC_ROBOT]        = cav->alien_score;
4710   level->score[SC_SPACESHIP]    = cav->tank_score;
4711   level->score[SC_BUG]          = cav->bug_score;
4712   level->score[SC_YAMYAM]       = cav->eater_score;
4713   level->score[SC_NUT]          = cav->nut_score;
4714   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4715   level->score[SC_KEY]          = cav->key_score;
4716   level->score[SC_TIME_BONUS]   = cav->exit_score;
4717
4718   level->num_yamyam_contents    = cav->num_eater_arrays;
4719
4720   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4721     for (y = 0; y < 3; y++)
4722       for (x = 0; x < 3; x++)
4723         level->yamyam_content[i].e[x][y] =
4724           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4725
4726   level->amoeba_speed           = cav->amoeba_time;
4727   level->time_magic_wall        = cav->wonderwall_time;
4728   level->time_wheel             = cav->wheel_time;
4729
4730   level->android_move_time      = cav->android_move_time;
4731   level->android_clone_time     = cav->android_clone_time;
4732   level->ball_random            = cav->ball_random;
4733   level->ball_active_initial    = cav->ball_active;
4734   level->ball_time              = cav->ball_time;
4735   level->num_ball_contents      = cav->num_ball_arrays;
4736
4737   level->lenses_score           = cav->lenses_score;
4738   level->magnify_score          = cav->magnify_score;
4739   level->slurp_score            = cav->slurp_score;
4740
4741   level->lenses_time            = cav->lenses_time;
4742   level->magnify_time           = cav->magnify_time;
4743
4744   level->wind_direction_initial =
4745     map_direction_EM_to_RND(cav->wind_direction);
4746
4747   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4748     for (j = 0; j < 8; j++)
4749       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4750         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4751
4752   map_android_clone_elements_EM_to_RND(level);
4753
4754   // convert the playfield (some elements need special treatment)
4755   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4756   {
4757     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4758
4759     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4760       new_element = EL_AMOEBA_DEAD;
4761
4762     level->field[x][y] = new_element;
4763   }
4764
4765   for (i = 0; i < MAX_PLAYERS; i++)
4766   {
4767     // in case of all players set to the same field, use the first player
4768     int nr = MAX_PLAYERS - i - 1;
4769     int jx = cav->player_x[nr];
4770     int jy = cav->player_y[nr];
4771
4772     if (jx != -1 && jy != -1)
4773       level->field[jx][jy] = EL_PLAYER_1 + nr;
4774   }
4775
4776   // time score is counted for each 10 seconds left in Emerald Mine levels
4777   level->time_score_base = 10;
4778 }
4779
4780
4781 // ----------------------------------------------------------------------------
4782 // functions for loading SP level
4783 // ----------------------------------------------------------------------------
4784
4785 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4786 {
4787   struct LevelInfo_SP *level_sp = level->native_sp_level;
4788   LevelInfoType *header = &level_sp->header;
4789   int i, x, y;
4790
4791   level_sp->width  = level->fieldx;
4792   level_sp->height = level->fieldy;
4793
4794   for (x = 0; x < level->fieldx; x++)
4795     for (y = 0; y < level->fieldy; y++)
4796       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4797
4798   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4799
4800   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4801     header->LevelTitle[i] = level->name[i];
4802   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4803
4804   header->InfotronsNeeded = level->gems_needed;
4805
4806   header->SpecialPortCount = 0;
4807
4808   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4809   {
4810     boolean gravity_port_found = FALSE;
4811     boolean gravity_port_valid = FALSE;
4812     int gravity_port_flag;
4813     int gravity_port_base_element;
4814     int element = level->field[x][y];
4815
4816     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4817         element <= EL_SP_GRAVITY_ON_PORT_UP)
4818     {
4819       gravity_port_found = TRUE;
4820       gravity_port_valid = TRUE;
4821       gravity_port_flag = 1;
4822       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4823     }
4824     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4825              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4826     {
4827       gravity_port_found = TRUE;
4828       gravity_port_valid = TRUE;
4829       gravity_port_flag = 0;
4830       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4831     }
4832     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4833              element <= EL_SP_GRAVITY_PORT_UP)
4834     {
4835       // change R'n'D style gravity inverting special port to normal port
4836       // (there are no gravity inverting ports in native Supaplex engine)
4837
4838       gravity_port_found = TRUE;
4839       gravity_port_valid = FALSE;
4840       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4841     }
4842
4843     if (gravity_port_found)
4844     {
4845       if (gravity_port_valid &&
4846           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4847       {
4848         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4849
4850         port->PortLocation = (y * level->fieldx + x) * 2;
4851         port->Gravity = gravity_port_flag;
4852
4853         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4854
4855         header->SpecialPortCount++;
4856       }
4857       else
4858       {
4859         // change special gravity port to normal port
4860
4861         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4862       }
4863
4864       level_sp->playfield[x][y] = element - EL_SP_START;
4865     }
4866   }
4867 }
4868
4869 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4870 {
4871   struct LevelInfo_SP *level_sp = level->native_sp_level;
4872   LevelInfoType *header = &level_sp->header;
4873   boolean num_invalid_elements = 0;
4874   int i, j, x, y;
4875
4876   level->fieldx = level_sp->width;
4877   level->fieldy = level_sp->height;
4878
4879   for (x = 0; x < level->fieldx; x++)
4880   {
4881     for (y = 0; y < level->fieldy; y++)
4882     {
4883       int element_old = level_sp->playfield[x][y];
4884       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4885
4886       if (element_new == EL_UNKNOWN)
4887       {
4888         num_invalid_elements++;
4889
4890         Debug("level:native:SP", "invalid element %d at position %d, %d",
4891               element_old, x, y);
4892       }
4893
4894       level->field[x][y] = element_new;
4895     }
4896   }
4897
4898   if (num_invalid_elements > 0)
4899     Warn("found %d invalid elements%s", num_invalid_elements,
4900          (!options.debug ? " (use '--debug' for more details)" : ""));
4901
4902   for (i = 0; i < MAX_PLAYERS; i++)
4903     level->initial_player_gravity[i] =
4904       (header->InitialGravity == 1 ? TRUE : FALSE);
4905
4906   // skip leading spaces
4907   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4908     if (header->LevelTitle[i] != ' ')
4909       break;
4910
4911   // copy level title
4912   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4913     level->name[j] = header->LevelTitle[i];
4914   level->name[j] = '\0';
4915
4916   // cut trailing spaces
4917   for (; j > 0; j--)
4918     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4919       level->name[j - 1] = '\0';
4920
4921   level->gems_needed = header->InfotronsNeeded;
4922
4923   for (i = 0; i < header->SpecialPortCount; i++)
4924   {
4925     SpecialPortType *port = &header->SpecialPort[i];
4926     int port_location = port->PortLocation;
4927     int gravity = port->Gravity;
4928     int port_x, port_y, port_element;
4929
4930     port_x = (port_location / 2) % level->fieldx;
4931     port_y = (port_location / 2) / level->fieldx;
4932
4933     if (port_x < 0 || port_x >= level->fieldx ||
4934         port_y < 0 || port_y >= level->fieldy)
4935     {
4936       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4937
4938       continue;
4939     }
4940
4941     port_element = level->field[port_x][port_y];
4942
4943     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4944         port_element > EL_SP_GRAVITY_PORT_UP)
4945     {
4946       Warn("no special port at position (%d, %d)", port_x, port_y);
4947
4948       continue;
4949     }
4950
4951     // change previous (wrong) gravity inverting special port to either
4952     // gravity enabling special port or gravity disabling special port
4953     level->field[port_x][port_y] +=
4954       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4955        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4956   }
4957
4958   // change special gravity ports without database entries to normal ports
4959   for (x = 0; x < level->fieldx; x++)
4960     for (y = 0; y < level->fieldy; y++)
4961       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4962           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4963         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4964
4965   level->time = 0;                      // no time limit
4966   level->amoeba_speed = 0;
4967   level->time_magic_wall = 0;
4968   level->time_wheel = 0;
4969   level->amoeba_content = EL_EMPTY;
4970
4971   // original Supaplex does not use score values -- rate by playing time
4972   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4973     level->score[i] = 0;
4974
4975   level->rate_time_over_score = TRUE;
4976
4977   // there are no yamyams in supaplex levels
4978   for (i = 0; i < level->num_yamyam_contents; i++)
4979     for (x = 0; x < 3; x++)
4980       for (y = 0; y < 3; y++)
4981         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4982 }
4983
4984 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4985 {
4986   struct LevelInfo_SP *level_sp = level->native_sp_level;
4987   struct DemoInfo_SP *demo = &level_sp->demo;
4988   int i, j;
4989
4990   // always start with reliable default values
4991   demo->is_available = FALSE;
4992   demo->length = 0;
4993
4994   if (TAPE_IS_EMPTY(tape))
4995     return;
4996
4997   demo->level_nr = tape.level_nr;       // (currently not used)
4998
4999   level_sp->header.DemoRandomSeed = tape.random_seed;
5000
5001   demo->length = 0;
5002
5003   for (i = 0; i < tape.length; i++)
5004   {
5005     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5006     int demo_repeat = tape.pos[i].delay;
5007     int demo_entries = (demo_repeat + 15) / 16;
5008
5009     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5010     {
5011       Warn("tape truncated: size exceeds maximum SP demo size %d",
5012            SP_MAX_TAPE_LEN);
5013
5014       break;
5015     }
5016
5017     for (j = 0; j < demo_repeat / 16; j++)
5018       demo->data[demo->length++] = 0xf0 | demo_action;
5019
5020     if (demo_repeat % 16)
5021       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5022   }
5023
5024   demo->is_available = TRUE;
5025 }
5026
5027 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5028 {
5029   struct LevelInfo_SP *level_sp = level->native_sp_level;
5030   struct DemoInfo_SP *demo = &level_sp->demo;
5031   char *filename = level->file_info.filename;
5032   int i;
5033
5034   // always start with reliable default values
5035   setTapeInfoToDefaults();
5036
5037   if (!demo->is_available)
5038     return;
5039
5040   tape.level_nr = demo->level_nr;       // (currently not used)
5041   tape.random_seed = level_sp->header.DemoRandomSeed;
5042
5043   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5044
5045   tape.counter = 0;
5046   tape.pos[tape.counter].delay = 0;
5047
5048   for (i = 0; i < demo->length; i++)
5049   {
5050     int demo_action = demo->data[i] & 0x0f;
5051     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5052     int tape_action = map_key_SP_to_RND(demo_action);
5053     int tape_repeat = demo_repeat + 1;
5054     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5055     boolean success = 0;
5056     int j;
5057
5058     for (j = 0; j < tape_repeat; j++)
5059       success = TapeAddAction(action);
5060
5061     if (!success)
5062     {
5063       Warn("SP demo truncated: size exceeds maximum tape size %d",
5064            MAX_TAPE_LEN);
5065
5066       break;
5067     }
5068   }
5069
5070   TapeHaltRecording();
5071 }
5072
5073
5074 // ----------------------------------------------------------------------------
5075 // functions for loading MM level
5076 // ----------------------------------------------------------------------------
5077
5078 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5079 {
5080   struct LevelInfo_MM *level_mm = level->native_mm_level;
5081   int i, x, y;
5082
5083   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5084   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5085
5086   level_mm->time = level->time;
5087   level_mm->kettles_needed = level->gems_needed;
5088   level_mm->auto_count_kettles = level->auto_count_gems;
5089
5090   level_mm->mm_laser_red   = level->mm_laser_red;
5091   level_mm->mm_laser_green = level->mm_laser_green;
5092   level_mm->mm_laser_blue  = level->mm_laser_blue;
5093
5094   level_mm->df_laser_red   = level->df_laser_red;
5095   level_mm->df_laser_green = level->df_laser_green;
5096   level_mm->df_laser_blue  = level->df_laser_blue;
5097
5098   strcpy(level_mm->name, level->name);
5099   strcpy(level_mm->author, level->author);
5100
5101   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5102   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5103   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5104   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5105   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5106
5107   level_mm->amoeba_speed = level->amoeba_speed;
5108   level_mm->time_fuse    = level->mm_time_fuse;
5109   level_mm->time_bomb    = level->mm_time_bomb;
5110   level_mm->time_ball    = level->mm_time_ball;
5111   level_mm->time_block   = level->mm_time_block;
5112
5113   level_mm->num_ball_contents = level->num_mm_ball_contents;
5114   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5115   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5116   level_mm->explode_ball = level->explode_mm_ball;
5117
5118   for (i = 0; i < level->num_mm_ball_contents; i++)
5119     level_mm->ball_content[i] =
5120       map_element_RND_to_MM(level->mm_ball_content[i]);
5121
5122   for (x = 0; x < level->fieldx; x++)
5123     for (y = 0; y < level->fieldy; y++)
5124       Ur[x][y] =
5125         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5126 }
5127
5128 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5129 {
5130   struct LevelInfo_MM *level_mm = level->native_mm_level;
5131   int i, x, y;
5132
5133   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5134   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5135
5136   level->time = level_mm->time;
5137   level->gems_needed = level_mm->kettles_needed;
5138   level->auto_count_gems = level_mm->auto_count_kettles;
5139
5140   level->mm_laser_red   = level_mm->mm_laser_red;
5141   level->mm_laser_green = level_mm->mm_laser_green;
5142   level->mm_laser_blue  = level_mm->mm_laser_blue;
5143
5144   level->df_laser_red   = level_mm->df_laser_red;
5145   level->df_laser_green = level_mm->df_laser_green;
5146   level->df_laser_blue  = level_mm->df_laser_blue;
5147
5148   strcpy(level->name, level_mm->name);
5149
5150   // only overwrite author from 'levelinfo.conf' if author defined in level
5151   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5152     strcpy(level->author, level_mm->author);
5153
5154   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5155   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5156   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5157   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5158   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5159
5160   level->amoeba_speed  = level_mm->amoeba_speed;
5161   level->mm_time_fuse  = level_mm->time_fuse;
5162   level->mm_time_bomb  = level_mm->time_bomb;
5163   level->mm_time_ball  = level_mm->time_ball;
5164   level->mm_time_block = level_mm->time_block;
5165
5166   level->num_mm_ball_contents = level_mm->num_ball_contents;
5167   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5168   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5169   level->explode_mm_ball = level_mm->explode_ball;
5170
5171   for (i = 0; i < level->num_mm_ball_contents; i++)
5172     level->mm_ball_content[i] =
5173       map_element_MM_to_RND(level_mm->ball_content[i]);
5174
5175   for (x = 0; x < level->fieldx; x++)
5176     for (y = 0; y < level->fieldy; y++)
5177       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5178 }
5179
5180
5181 // ----------------------------------------------------------------------------
5182 // functions for loading DC level
5183 // ----------------------------------------------------------------------------
5184
5185 #define DC_LEVEL_HEADER_SIZE            344
5186
5187 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5188                                         boolean init)
5189 {
5190   static int last_data_encoded;
5191   static int offset1;
5192   static int offset2;
5193   int diff;
5194   int diff_hi, diff_lo;
5195   int data_hi, data_lo;
5196   unsigned short data_decoded;
5197
5198   if (init)
5199   {
5200     last_data_encoded = 0;
5201     offset1 = -1;
5202     offset2 = 0;
5203
5204     return 0;
5205   }
5206
5207   diff = data_encoded - last_data_encoded;
5208   diff_hi = diff & ~0xff;
5209   diff_lo = diff &  0xff;
5210
5211   offset2 += diff_lo;
5212
5213   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5214   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5215   data_hi = data_hi & 0xff00;
5216
5217   data_decoded = data_hi | data_lo;
5218
5219   last_data_encoded = data_encoded;
5220
5221   offset1 = (offset1 + 1) % 31;
5222   offset2 = offset2 & 0xff;
5223
5224   return data_decoded;
5225 }
5226
5227 static int getMappedElement_DC(int element)
5228 {
5229   switch (element)
5230   {
5231     case 0x0000:
5232       element = EL_ROCK;
5233       break;
5234
5235       // 0x0117 - 0x036e: (?)
5236       // EL_DIAMOND
5237
5238       // 0x042d - 0x0684: (?)
5239       // EL_EMERALD
5240
5241     case 0x06f1:
5242       element = EL_NUT;
5243       break;
5244
5245     case 0x074c:
5246       element = EL_BOMB;
5247       break;
5248
5249     case 0x07a4:
5250       element = EL_PEARL;
5251       break;
5252
5253     case 0x0823:
5254       element = EL_CRYSTAL;
5255       break;
5256
5257     case 0x0e77:        // quicksand (boulder)
5258       element = EL_QUICKSAND_FAST_FULL;
5259       break;
5260
5261     case 0x0e99:        // slow quicksand (boulder)
5262       element = EL_QUICKSAND_FULL;
5263       break;
5264
5265     case 0x0ed2:
5266       element = EL_EM_EXIT_OPEN;
5267       break;
5268
5269     case 0x0ee3:
5270       element = EL_EM_EXIT_CLOSED;
5271       break;
5272
5273     case 0x0eeb:
5274       element = EL_EM_STEEL_EXIT_OPEN;
5275       break;
5276
5277     case 0x0efc:
5278       element = EL_EM_STEEL_EXIT_CLOSED;
5279       break;
5280
5281     case 0x0f4f:        // dynamite (lit 1)
5282       element = EL_EM_DYNAMITE_ACTIVE;
5283       break;
5284
5285     case 0x0f57:        // dynamite (lit 2)
5286       element = EL_EM_DYNAMITE_ACTIVE;
5287       break;
5288
5289     case 0x0f5f:        // dynamite (lit 3)
5290       element = EL_EM_DYNAMITE_ACTIVE;
5291       break;
5292
5293     case 0x0f67:        // dynamite (lit 4)
5294       element = EL_EM_DYNAMITE_ACTIVE;
5295       break;
5296
5297     case 0x0f81:
5298     case 0x0f82:
5299     case 0x0f83:
5300     case 0x0f84:
5301       element = EL_AMOEBA_WET;
5302       break;
5303
5304     case 0x0f85:
5305       element = EL_AMOEBA_DROP;
5306       break;
5307
5308     case 0x0fb9:
5309       element = EL_DC_MAGIC_WALL;
5310       break;
5311
5312     case 0x0fd0:
5313       element = EL_SPACESHIP_UP;
5314       break;
5315
5316     case 0x0fd9:
5317       element = EL_SPACESHIP_DOWN;
5318       break;
5319
5320     case 0x0ff1:
5321       element = EL_SPACESHIP_LEFT;
5322       break;
5323
5324     case 0x0ff9:
5325       element = EL_SPACESHIP_RIGHT;
5326       break;
5327
5328     case 0x1057:
5329       element = EL_BUG_UP;
5330       break;
5331
5332     case 0x1060:
5333       element = EL_BUG_DOWN;
5334       break;
5335
5336     case 0x1078:
5337       element = EL_BUG_LEFT;
5338       break;
5339
5340     case 0x1080:
5341       element = EL_BUG_RIGHT;
5342       break;
5343
5344     case 0x10de:
5345       element = EL_MOLE_UP;
5346       break;
5347
5348     case 0x10e7:
5349       element = EL_MOLE_DOWN;
5350       break;
5351
5352     case 0x10ff:
5353       element = EL_MOLE_LEFT;
5354       break;
5355
5356     case 0x1107:
5357       element = EL_MOLE_RIGHT;
5358       break;
5359
5360     case 0x11c0:
5361       element = EL_ROBOT;
5362       break;
5363
5364     case 0x13f5:
5365       element = EL_YAMYAM_UP;
5366       break;
5367
5368     case 0x1425:
5369       element = EL_SWITCHGATE_OPEN;
5370       break;
5371
5372     case 0x1426:
5373       element = EL_SWITCHGATE_CLOSED;
5374       break;
5375
5376     case 0x1437:
5377       element = EL_DC_SWITCHGATE_SWITCH_UP;
5378       break;
5379
5380     case 0x143a:
5381       element = EL_TIMEGATE_CLOSED;
5382       break;
5383
5384     case 0x144c:        // conveyor belt switch (green)
5385       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5386       break;
5387
5388     case 0x144f:        // conveyor belt switch (red)
5389       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5390       break;
5391
5392     case 0x1452:        // conveyor belt switch (blue)
5393       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5394       break;
5395
5396     case 0x145b:
5397       element = EL_CONVEYOR_BELT_3_MIDDLE;
5398       break;
5399
5400     case 0x1463:
5401       element = EL_CONVEYOR_BELT_3_LEFT;
5402       break;
5403
5404     case 0x146b:
5405       element = EL_CONVEYOR_BELT_3_RIGHT;
5406       break;
5407
5408     case 0x1473:
5409       element = EL_CONVEYOR_BELT_1_MIDDLE;
5410       break;
5411
5412     case 0x147b:
5413       element = EL_CONVEYOR_BELT_1_LEFT;
5414       break;
5415
5416     case 0x1483:
5417       element = EL_CONVEYOR_BELT_1_RIGHT;
5418       break;
5419
5420     case 0x148b:
5421       element = EL_CONVEYOR_BELT_4_MIDDLE;
5422       break;
5423
5424     case 0x1493:
5425       element = EL_CONVEYOR_BELT_4_LEFT;
5426       break;
5427
5428     case 0x149b:
5429       element = EL_CONVEYOR_BELT_4_RIGHT;
5430       break;
5431
5432     case 0x14ac:
5433       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5434       break;
5435
5436     case 0x14bd:
5437       element = EL_EXPANDABLE_WALL_VERTICAL;
5438       break;
5439
5440     case 0x14c6:
5441       element = EL_EXPANDABLE_WALL_ANY;
5442       break;
5443
5444     case 0x14ce:        // growing steel wall (left/right)
5445       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5446       break;
5447
5448     case 0x14df:        // growing steel wall (up/down)
5449       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5450       break;
5451
5452     case 0x14e8:        // growing steel wall (up/down/left/right)
5453       element = EL_EXPANDABLE_STEELWALL_ANY;
5454       break;
5455
5456     case 0x14e9:
5457       element = EL_SHIELD_DEADLY;
5458       break;
5459
5460     case 0x1501:
5461       element = EL_EXTRA_TIME;
5462       break;
5463
5464     case 0x154f:
5465       element = EL_ACID;
5466       break;
5467
5468     case 0x1577:
5469       element = EL_EMPTY_SPACE;
5470       break;
5471
5472     case 0x1578:        // quicksand (empty)
5473       element = EL_QUICKSAND_FAST_EMPTY;
5474       break;
5475
5476     case 0x1579:        // slow quicksand (empty)
5477       element = EL_QUICKSAND_EMPTY;
5478       break;
5479
5480       // 0x157c - 0x158b:
5481       // EL_SAND
5482
5483       // 0x1590 - 0x159f:
5484       // EL_DC_LANDMINE
5485
5486     case 0x15a0:
5487       element = EL_EM_DYNAMITE;
5488       break;
5489
5490     case 0x15a1:        // key (red)
5491       element = EL_EM_KEY_1;
5492       break;
5493
5494     case 0x15a2:        // key (yellow)
5495       element = EL_EM_KEY_2;
5496       break;
5497
5498     case 0x15a3:        // key (blue)
5499       element = EL_EM_KEY_4;
5500       break;
5501
5502     case 0x15a4:        // key (green)
5503       element = EL_EM_KEY_3;
5504       break;
5505
5506     case 0x15a5:        // key (white)
5507       element = EL_DC_KEY_WHITE;
5508       break;
5509
5510     case 0x15a6:
5511       element = EL_WALL_SLIPPERY;
5512       break;
5513
5514     case 0x15a7:
5515       element = EL_WALL;
5516       break;
5517
5518     case 0x15a8:        // wall (not round)
5519       element = EL_WALL;
5520       break;
5521
5522     case 0x15a9:        // (blue)
5523       element = EL_CHAR_A;
5524       break;
5525
5526     case 0x15aa:        // (blue)
5527       element = EL_CHAR_B;
5528       break;
5529
5530     case 0x15ab:        // (blue)
5531       element = EL_CHAR_C;
5532       break;
5533
5534     case 0x15ac:        // (blue)
5535       element = EL_CHAR_D;
5536       break;
5537
5538     case 0x15ad:        // (blue)
5539       element = EL_CHAR_E;
5540       break;
5541
5542     case 0x15ae:        // (blue)
5543       element = EL_CHAR_F;
5544       break;
5545
5546     case 0x15af:        // (blue)
5547       element = EL_CHAR_G;
5548       break;
5549
5550     case 0x15b0:        // (blue)
5551       element = EL_CHAR_H;
5552       break;
5553
5554     case 0x15b1:        // (blue)
5555       element = EL_CHAR_I;
5556       break;
5557
5558     case 0x15b2:        // (blue)
5559       element = EL_CHAR_J;
5560       break;
5561
5562     case 0x15b3:        // (blue)
5563       element = EL_CHAR_K;
5564       break;
5565
5566     case 0x15b4:        // (blue)
5567       element = EL_CHAR_L;
5568       break;
5569
5570     case 0x15b5:        // (blue)
5571       element = EL_CHAR_M;
5572       break;
5573
5574     case 0x15b6:        // (blue)
5575       element = EL_CHAR_N;
5576       break;
5577
5578     case 0x15b7:        // (blue)
5579       element = EL_CHAR_O;
5580       break;
5581
5582     case 0x15b8:        // (blue)
5583       element = EL_CHAR_P;
5584       break;
5585
5586     case 0x15b9:        // (blue)
5587       element = EL_CHAR_Q;
5588       break;
5589
5590     case 0x15ba:        // (blue)
5591       element = EL_CHAR_R;
5592       break;
5593
5594     case 0x15bb:        // (blue)
5595       element = EL_CHAR_S;
5596       break;
5597
5598     case 0x15bc:        // (blue)
5599       element = EL_CHAR_T;
5600       break;
5601
5602     case 0x15bd:        // (blue)
5603       element = EL_CHAR_U;
5604       break;
5605
5606     case 0x15be:        // (blue)
5607       element = EL_CHAR_V;
5608       break;
5609
5610     case 0x15bf:        // (blue)
5611       element = EL_CHAR_W;
5612       break;
5613
5614     case 0x15c0:        // (blue)
5615       element = EL_CHAR_X;
5616       break;
5617
5618     case 0x15c1:        // (blue)
5619       element = EL_CHAR_Y;
5620       break;
5621
5622     case 0x15c2:        // (blue)
5623       element = EL_CHAR_Z;
5624       break;
5625
5626     case 0x15c3:        // (blue)
5627       element = EL_CHAR_AUMLAUT;
5628       break;
5629
5630     case 0x15c4:        // (blue)
5631       element = EL_CHAR_OUMLAUT;
5632       break;
5633
5634     case 0x15c5:        // (blue)
5635       element = EL_CHAR_UUMLAUT;
5636       break;
5637
5638     case 0x15c6:        // (blue)
5639       element = EL_CHAR_0;
5640       break;
5641
5642     case 0x15c7:        // (blue)
5643       element = EL_CHAR_1;
5644       break;
5645
5646     case 0x15c8:        // (blue)
5647       element = EL_CHAR_2;
5648       break;
5649
5650     case 0x15c9:        // (blue)
5651       element = EL_CHAR_3;
5652       break;
5653
5654     case 0x15ca:        // (blue)
5655       element = EL_CHAR_4;
5656       break;
5657
5658     case 0x15cb:        // (blue)
5659       element = EL_CHAR_5;
5660       break;
5661
5662     case 0x15cc:        // (blue)
5663       element = EL_CHAR_6;
5664       break;
5665
5666     case 0x15cd:        // (blue)
5667       element = EL_CHAR_7;
5668       break;
5669
5670     case 0x15ce:        // (blue)
5671       element = EL_CHAR_8;
5672       break;
5673
5674     case 0x15cf:        // (blue)
5675       element = EL_CHAR_9;
5676       break;
5677
5678     case 0x15d0:        // (blue)
5679       element = EL_CHAR_PERIOD;
5680       break;
5681
5682     case 0x15d1:        // (blue)
5683       element = EL_CHAR_EXCLAM;
5684       break;
5685
5686     case 0x15d2:        // (blue)
5687       element = EL_CHAR_COLON;
5688       break;
5689
5690     case 0x15d3:        // (blue)
5691       element = EL_CHAR_LESS;
5692       break;
5693
5694     case 0x15d4:        // (blue)
5695       element = EL_CHAR_GREATER;
5696       break;
5697
5698     case 0x15d5:        // (blue)
5699       element = EL_CHAR_QUESTION;
5700       break;
5701
5702     case 0x15d6:        // (blue)
5703       element = EL_CHAR_COPYRIGHT;
5704       break;
5705
5706     case 0x15d7:        // (blue)
5707       element = EL_CHAR_UP;
5708       break;
5709
5710     case 0x15d8:        // (blue)
5711       element = EL_CHAR_DOWN;
5712       break;
5713
5714     case 0x15d9:        // (blue)
5715       element = EL_CHAR_BUTTON;
5716       break;
5717
5718     case 0x15da:        // (blue)
5719       element = EL_CHAR_PLUS;
5720       break;
5721
5722     case 0x15db:        // (blue)
5723       element = EL_CHAR_MINUS;
5724       break;
5725
5726     case 0x15dc:        // (blue)
5727       element = EL_CHAR_APOSTROPHE;
5728       break;
5729
5730     case 0x15dd:        // (blue)
5731       element = EL_CHAR_PARENLEFT;
5732       break;
5733
5734     case 0x15de:        // (blue)
5735       element = EL_CHAR_PARENRIGHT;
5736       break;
5737
5738     case 0x15df:        // (green)
5739       element = EL_CHAR_A;
5740       break;
5741
5742     case 0x15e0:        // (green)
5743       element = EL_CHAR_B;
5744       break;
5745
5746     case 0x15e1:        // (green)
5747       element = EL_CHAR_C;
5748       break;
5749
5750     case 0x15e2:        // (green)
5751       element = EL_CHAR_D;
5752       break;
5753
5754     case 0x15e3:        // (green)
5755       element = EL_CHAR_E;
5756       break;
5757
5758     case 0x15e4:        // (green)
5759       element = EL_CHAR_F;
5760       break;
5761
5762     case 0x15e5:        // (green)
5763       element = EL_CHAR_G;
5764       break;
5765
5766     case 0x15e6:        // (green)
5767       element = EL_CHAR_H;
5768       break;
5769
5770     case 0x15e7:        // (green)
5771       element = EL_CHAR_I;
5772       break;
5773
5774     case 0x15e8:        // (green)
5775       element = EL_CHAR_J;
5776       break;
5777
5778     case 0x15e9:        // (green)
5779       element = EL_CHAR_K;
5780       break;
5781
5782     case 0x15ea:        // (green)
5783       element = EL_CHAR_L;
5784       break;
5785
5786     case 0x15eb:        // (green)
5787       element = EL_CHAR_M;
5788       break;
5789
5790     case 0x15ec:        // (green)
5791       element = EL_CHAR_N;
5792       break;
5793
5794     case 0x15ed:        // (green)
5795       element = EL_CHAR_O;
5796       break;
5797
5798     case 0x15ee:        // (green)
5799       element = EL_CHAR_P;
5800       break;
5801
5802     case 0x15ef:        // (green)
5803       element = EL_CHAR_Q;
5804       break;
5805
5806     case 0x15f0:        // (green)
5807       element = EL_CHAR_R;
5808       break;
5809
5810     case 0x15f1:        // (green)
5811       element = EL_CHAR_S;
5812       break;
5813
5814     case 0x15f2:        // (green)
5815       element = EL_CHAR_T;
5816       break;
5817
5818     case 0x15f3:        // (green)
5819       element = EL_CHAR_U;
5820       break;
5821
5822     case 0x15f4:        // (green)
5823       element = EL_CHAR_V;
5824       break;
5825
5826     case 0x15f5:        // (green)
5827       element = EL_CHAR_W;
5828       break;
5829
5830     case 0x15f6:        // (green)
5831       element = EL_CHAR_X;
5832       break;
5833
5834     case 0x15f7:        // (green)
5835       element = EL_CHAR_Y;
5836       break;
5837
5838     case 0x15f8:        // (green)
5839       element = EL_CHAR_Z;
5840       break;
5841
5842     case 0x15f9:        // (green)
5843       element = EL_CHAR_AUMLAUT;
5844       break;
5845
5846     case 0x15fa:        // (green)
5847       element = EL_CHAR_OUMLAUT;
5848       break;
5849
5850     case 0x15fb:        // (green)
5851       element = EL_CHAR_UUMLAUT;
5852       break;
5853
5854     case 0x15fc:        // (green)
5855       element = EL_CHAR_0;
5856       break;
5857
5858     case 0x15fd:        // (green)
5859       element = EL_CHAR_1;
5860       break;
5861
5862     case 0x15fe:        // (green)
5863       element = EL_CHAR_2;
5864       break;
5865
5866     case 0x15ff:        // (green)
5867       element = EL_CHAR_3;
5868       break;
5869
5870     case 0x1600:        // (green)
5871       element = EL_CHAR_4;
5872       break;
5873
5874     case 0x1601:        // (green)
5875       element = EL_CHAR_5;
5876       break;
5877
5878     case 0x1602:        // (green)
5879       element = EL_CHAR_6;
5880       break;
5881
5882     case 0x1603:        // (green)
5883       element = EL_CHAR_7;
5884       break;
5885
5886     case 0x1604:        // (green)
5887       element = EL_CHAR_8;
5888       break;
5889
5890     case 0x1605:        // (green)
5891       element = EL_CHAR_9;
5892       break;
5893
5894     case 0x1606:        // (green)
5895       element = EL_CHAR_PERIOD;
5896       break;
5897
5898     case 0x1607:        // (green)
5899       element = EL_CHAR_EXCLAM;
5900       break;
5901
5902     case 0x1608:        // (green)
5903       element = EL_CHAR_COLON;
5904       break;
5905
5906     case 0x1609:        // (green)
5907       element = EL_CHAR_LESS;
5908       break;
5909
5910     case 0x160a:        // (green)
5911       element = EL_CHAR_GREATER;
5912       break;
5913
5914     case 0x160b:        // (green)
5915       element = EL_CHAR_QUESTION;
5916       break;
5917
5918     case 0x160c:        // (green)
5919       element = EL_CHAR_COPYRIGHT;
5920       break;
5921
5922     case 0x160d:        // (green)
5923       element = EL_CHAR_UP;
5924       break;
5925
5926     case 0x160e:        // (green)
5927       element = EL_CHAR_DOWN;
5928       break;
5929
5930     case 0x160f:        // (green)
5931       element = EL_CHAR_BUTTON;
5932       break;
5933
5934     case 0x1610:        // (green)
5935       element = EL_CHAR_PLUS;
5936       break;
5937
5938     case 0x1611:        // (green)
5939       element = EL_CHAR_MINUS;
5940       break;
5941
5942     case 0x1612:        // (green)
5943       element = EL_CHAR_APOSTROPHE;
5944       break;
5945
5946     case 0x1613:        // (green)
5947       element = EL_CHAR_PARENLEFT;
5948       break;
5949
5950     case 0x1614:        // (green)
5951       element = EL_CHAR_PARENRIGHT;
5952       break;
5953
5954     case 0x1615:        // (blue steel)
5955       element = EL_STEEL_CHAR_A;
5956       break;
5957
5958     case 0x1616:        // (blue steel)
5959       element = EL_STEEL_CHAR_B;
5960       break;
5961
5962     case 0x1617:        // (blue steel)
5963       element = EL_STEEL_CHAR_C;
5964       break;
5965
5966     case 0x1618:        // (blue steel)
5967       element = EL_STEEL_CHAR_D;
5968       break;
5969
5970     case 0x1619:        // (blue steel)
5971       element = EL_STEEL_CHAR_E;
5972       break;
5973
5974     case 0x161a:        // (blue steel)
5975       element = EL_STEEL_CHAR_F;
5976       break;
5977
5978     case 0x161b:        // (blue steel)
5979       element = EL_STEEL_CHAR_G;
5980       break;
5981
5982     case 0x161c:        // (blue steel)
5983       element = EL_STEEL_CHAR_H;
5984       break;
5985
5986     case 0x161d:        // (blue steel)
5987       element = EL_STEEL_CHAR_I;
5988       break;
5989
5990     case 0x161e:        // (blue steel)
5991       element = EL_STEEL_CHAR_J;
5992       break;
5993
5994     case 0x161f:        // (blue steel)
5995       element = EL_STEEL_CHAR_K;
5996       break;
5997
5998     case 0x1620:        // (blue steel)
5999       element = EL_STEEL_CHAR_L;
6000       break;
6001
6002     case 0x1621:        // (blue steel)
6003       element = EL_STEEL_CHAR_M;
6004       break;
6005
6006     case 0x1622:        // (blue steel)
6007       element = EL_STEEL_CHAR_N;
6008       break;
6009
6010     case 0x1623:        // (blue steel)
6011       element = EL_STEEL_CHAR_O;
6012       break;
6013
6014     case 0x1624:        // (blue steel)
6015       element = EL_STEEL_CHAR_P;
6016       break;
6017
6018     case 0x1625:        // (blue steel)
6019       element = EL_STEEL_CHAR_Q;
6020       break;
6021
6022     case 0x1626:        // (blue steel)
6023       element = EL_STEEL_CHAR_R;
6024       break;
6025
6026     case 0x1627:        // (blue steel)
6027       element = EL_STEEL_CHAR_S;
6028       break;
6029
6030     case 0x1628:        // (blue steel)
6031       element = EL_STEEL_CHAR_T;
6032       break;
6033
6034     case 0x1629:        // (blue steel)
6035       element = EL_STEEL_CHAR_U;
6036       break;
6037
6038     case 0x162a:        // (blue steel)
6039       element = EL_STEEL_CHAR_V;
6040       break;
6041
6042     case 0x162b:        // (blue steel)
6043       element = EL_STEEL_CHAR_W;
6044       break;
6045
6046     case 0x162c:        // (blue steel)
6047       element = EL_STEEL_CHAR_X;
6048       break;
6049
6050     case 0x162d:        // (blue steel)
6051       element = EL_STEEL_CHAR_Y;
6052       break;
6053
6054     case 0x162e:        // (blue steel)
6055       element = EL_STEEL_CHAR_Z;
6056       break;
6057
6058     case 0x162f:        // (blue steel)
6059       element = EL_STEEL_CHAR_AUMLAUT;
6060       break;
6061
6062     case 0x1630:        // (blue steel)
6063       element = EL_STEEL_CHAR_OUMLAUT;
6064       break;
6065
6066     case 0x1631:        // (blue steel)
6067       element = EL_STEEL_CHAR_UUMLAUT;
6068       break;
6069
6070     case 0x1632:        // (blue steel)
6071       element = EL_STEEL_CHAR_0;
6072       break;
6073
6074     case 0x1633:        // (blue steel)
6075       element = EL_STEEL_CHAR_1;
6076       break;
6077
6078     case 0x1634:        // (blue steel)
6079       element = EL_STEEL_CHAR_2;
6080       break;
6081
6082     case 0x1635:        // (blue steel)
6083       element = EL_STEEL_CHAR_3;
6084       break;
6085
6086     case 0x1636:        // (blue steel)
6087       element = EL_STEEL_CHAR_4;
6088       break;
6089
6090     case 0x1637:        // (blue steel)
6091       element = EL_STEEL_CHAR_5;
6092       break;
6093
6094     case 0x1638:        // (blue steel)
6095       element = EL_STEEL_CHAR_6;
6096       break;
6097
6098     case 0x1639:        // (blue steel)
6099       element = EL_STEEL_CHAR_7;
6100       break;
6101
6102     case 0x163a:        // (blue steel)
6103       element = EL_STEEL_CHAR_8;
6104       break;
6105
6106     case 0x163b:        // (blue steel)
6107       element = EL_STEEL_CHAR_9;
6108       break;
6109
6110     case 0x163c:        // (blue steel)
6111       element = EL_STEEL_CHAR_PERIOD;
6112       break;
6113
6114     case 0x163d:        // (blue steel)
6115       element = EL_STEEL_CHAR_EXCLAM;
6116       break;
6117
6118     case 0x163e:        // (blue steel)
6119       element = EL_STEEL_CHAR_COLON;
6120       break;
6121
6122     case 0x163f:        // (blue steel)
6123       element = EL_STEEL_CHAR_LESS;
6124       break;
6125
6126     case 0x1640:        // (blue steel)
6127       element = EL_STEEL_CHAR_GREATER;
6128       break;
6129
6130     case 0x1641:        // (blue steel)
6131       element = EL_STEEL_CHAR_QUESTION;
6132       break;
6133
6134     case 0x1642:        // (blue steel)
6135       element = EL_STEEL_CHAR_COPYRIGHT;
6136       break;
6137
6138     case 0x1643:        // (blue steel)
6139       element = EL_STEEL_CHAR_UP;
6140       break;
6141
6142     case 0x1644:        // (blue steel)
6143       element = EL_STEEL_CHAR_DOWN;
6144       break;
6145
6146     case 0x1645:        // (blue steel)
6147       element = EL_STEEL_CHAR_BUTTON;
6148       break;
6149
6150     case 0x1646:        // (blue steel)
6151       element = EL_STEEL_CHAR_PLUS;
6152       break;
6153
6154     case 0x1647:        // (blue steel)
6155       element = EL_STEEL_CHAR_MINUS;
6156       break;
6157
6158     case 0x1648:        // (blue steel)
6159       element = EL_STEEL_CHAR_APOSTROPHE;
6160       break;
6161
6162     case 0x1649:        // (blue steel)
6163       element = EL_STEEL_CHAR_PARENLEFT;
6164       break;
6165
6166     case 0x164a:        // (blue steel)
6167       element = EL_STEEL_CHAR_PARENRIGHT;
6168       break;
6169
6170     case 0x164b:        // (green steel)
6171       element = EL_STEEL_CHAR_A;
6172       break;
6173
6174     case 0x164c:        // (green steel)
6175       element = EL_STEEL_CHAR_B;
6176       break;
6177
6178     case 0x164d:        // (green steel)
6179       element = EL_STEEL_CHAR_C;
6180       break;
6181
6182     case 0x164e:        // (green steel)
6183       element = EL_STEEL_CHAR_D;
6184       break;
6185
6186     case 0x164f:        // (green steel)
6187       element = EL_STEEL_CHAR_E;
6188       break;
6189
6190     case 0x1650:        // (green steel)
6191       element = EL_STEEL_CHAR_F;
6192       break;
6193
6194     case 0x1651:        // (green steel)
6195       element = EL_STEEL_CHAR_G;
6196       break;
6197
6198     case 0x1652:        // (green steel)
6199       element = EL_STEEL_CHAR_H;
6200       break;
6201
6202     case 0x1653:        // (green steel)
6203       element = EL_STEEL_CHAR_I;
6204       break;
6205
6206     case 0x1654:        // (green steel)
6207       element = EL_STEEL_CHAR_J;
6208       break;
6209
6210     case 0x1655:        // (green steel)
6211       element = EL_STEEL_CHAR_K;
6212       break;
6213
6214     case 0x1656:        // (green steel)
6215       element = EL_STEEL_CHAR_L;
6216       break;
6217
6218     case 0x1657:        // (green steel)
6219       element = EL_STEEL_CHAR_M;
6220       break;
6221
6222     case 0x1658:        // (green steel)
6223       element = EL_STEEL_CHAR_N;
6224       break;
6225
6226     case 0x1659:        // (green steel)
6227       element = EL_STEEL_CHAR_O;
6228       break;
6229
6230     case 0x165a:        // (green steel)
6231       element = EL_STEEL_CHAR_P;
6232       break;
6233
6234     case 0x165b:        // (green steel)
6235       element = EL_STEEL_CHAR_Q;
6236       break;
6237
6238     case 0x165c:        // (green steel)
6239       element = EL_STEEL_CHAR_R;
6240       break;
6241
6242     case 0x165d:        // (green steel)
6243       element = EL_STEEL_CHAR_S;
6244       break;
6245
6246     case 0x165e:        // (green steel)
6247       element = EL_STEEL_CHAR_T;
6248       break;
6249
6250     case 0x165f:        // (green steel)
6251       element = EL_STEEL_CHAR_U;
6252       break;
6253
6254     case 0x1660:        // (green steel)
6255       element = EL_STEEL_CHAR_V;
6256       break;
6257
6258     case 0x1661:        // (green steel)
6259       element = EL_STEEL_CHAR_W;
6260       break;
6261
6262     case 0x1662:        // (green steel)
6263       element = EL_STEEL_CHAR_X;
6264       break;
6265
6266     case 0x1663:        // (green steel)
6267       element = EL_STEEL_CHAR_Y;
6268       break;
6269
6270     case 0x1664:        // (green steel)
6271       element = EL_STEEL_CHAR_Z;
6272       break;
6273
6274     case 0x1665:        // (green steel)
6275       element = EL_STEEL_CHAR_AUMLAUT;
6276       break;
6277
6278     case 0x1666:        // (green steel)
6279       element = EL_STEEL_CHAR_OUMLAUT;
6280       break;
6281
6282     case 0x1667:        // (green steel)
6283       element = EL_STEEL_CHAR_UUMLAUT;
6284       break;
6285
6286     case 0x1668:        // (green steel)
6287       element = EL_STEEL_CHAR_0;
6288       break;
6289
6290     case 0x1669:        // (green steel)
6291       element = EL_STEEL_CHAR_1;
6292       break;
6293
6294     case 0x166a:        // (green steel)
6295       element = EL_STEEL_CHAR_2;
6296       break;
6297
6298     case 0x166b:        // (green steel)
6299       element = EL_STEEL_CHAR_3;
6300       break;
6301
6302     case 0x166c:        // (green steel)
6303       element = EL_STEEL_CHAR_4;
6304       break;
6305
6306     case 0x166d:        // (green steel)
6307       element = EL_STEEL_CHAR_5;
6308       break;
6309
6310     case 0x166e:        // (green steel)
6311       element = EL_STEEL_CHAR_6;
6312       break;
6313
6314     case 0x166f:        // (green steel)
6315       element = EL_STEEL_CHAR_7;
6316       break;
6317
6318     case 0x1670:        // (green steel)
6319       element = EL_STEEL_CHAR_8;
6320       break;
6321
6322     case 0x1671:        // (green steel)
6323       element = EL_STEEL_CHAR_9;
6324       break;
6325
6326     case 0x1672:        // (green steel)
6327       element = EL_STEEL_CHAR_PERIOD;
6328       break;
6329
6330     case 0x1673:        // (green steel)
6331       element = EL_STEEL_CHAR_EXCLAM;
6332       break;
6333
6334     case 0x1674:        // (green steel)
6335       element = EL_STEEL_CHAR_COLON;
6336       break;
6337
6338     case 0x1675:        // (green steel)
6339       element = EL_STEEL_CHAR_LESS;
6340       break;
6341
6342     case 0x1676:        // (green steel)
6343       element = EL_STEEL_CHAR_GREATER;
6344       break;
6345
6346     case 0x1677:        // (green steel)
6347       element = EL_STEEL_CHAR_QUESTION;
6348       break;
6349
6350     case 0x1678:        // (green steel)
6351       element = EL_STEEL_CHAR_COPYRIGHT;
6352       break;
6353
6354     case 0x1679:        // (green steel)
6355       element = EL_STEEL_CHAR_UP;
6356       break;
6357
6358     case 0x167a:        // (green steel)
6359       element = EL_STEEL_CHAR_DOWN;
6360       break;
6361
6362     case 0x167b:        // (green steel)
6363       element = EL_STEEL_CHAR_BUTTON;
6364       break;
6365
6366     case 0x167c:        // (green steel)
6367       element = EL_STEEL_CHAR_PLUS;
6368       break;
6369
6370     case 0x167d:        // (green steel)
6371       element = EL_STEEL_CHAR_MINUS;
6372       break;
6373
6374     case 0x167e:        // (green steel)
6375       element = EL_STEEL_CHAR_APOSTROPHE;
6376       break;
6377
6378     case 0x167f:        // (green steel)
6379       element = EL_STEEL_CHAR_PARENLEFT;
6380       break;
6381
6382     case 0x1680:        // (green steel)
6383       element = EL_STEEL_CHAR_PARENRIGHT;
6384       break;
6385
6386     case 0x1681:        // gate (red)
6387       element = EL_EM_GATE_1;
6388       break;
6389
6390     case 0x1682:        // secret gate (red)
6391       element = EL_EM_GATE_1_GRAY;
6392       break;
6393
6394     case 0x1683:        // gate (yellow)
6395       element = EL_EM_GATE_2;
6396       break;
6397
6398     case 0x1684:        // secret gate (yellow)
6399       element = EL_EM_GATE_2_GRAY;
6400       break;
6401
6402     case 0x1685:        // gate (blue)
6403       element = EL_EM_GATE_4;
6404       break;
6405
6406     case 0x1686:        // secret gate (blue)
6407       element = EL_EM_GATE_4_GRAY;
6408       break;
6409
6410     case 0x1687:        // gate (green)
6411       element = EL_EM_GATE_3;
6412       break;
6413
6414     case 0x1688:        // secret gate (green)
6415       element = EL_EM_GATE_3_GRAY;
6416       break;
6417
6418     case 0x1689:        // gate (white)
6419       element = EL_DC_GATE_WHITE;
6420       break;
6421
6422     case 0x168a:        // secret gate (white)
6423       element = EL_DC_GATE_WHITE_GRAY;
6424       break;
6425
6426     case 0x168b:        // secret gate (no key)
6427       element = EL_DC_GATE_FAKE_GRAY;
6428       break;
6429
6430     case 0x168c:
6431       element = EL_ROBOT_WHEEL;
6432       break;
6433
6434     case 0x168d:
6435       element = EL_DC_TIMEGATE_SWITCH;
6436       break;
6437
6438     case 0x168e:
6439       element = EL_ACID_POOL_BOTTOM;
6440       break;
6441
6442     case 0x168f:
6443       element = EL_ACID_POOL_TOPLEFT;
6444       break;
6445
6446     case 0x1690:
6447       element = EL_ACID_POOL_TOPRIGHT;
6448       break;
6449
6450     case 0x1691:
6451       element = EL_ACID_POOL_BOTTOMLEFT;
6452       break;
6453
6454     case 0x1692:
6455       element = EL_ACID_POOL_BOTTOMRIGHT;
6456       break;
6457
6458     case 0x1693:
6459       element = EL_STEELWALL;
6460       break;
6461
6462     case 0x1694:
6463       element = EL_STEELWALL_SLIPPERY;
6464       break;
6465
6466     case 0x1695:        // steel wall (not round)
6467       element = EL_STEELWALL;
6468       break;
6469
6470     case 0x1696:        // steel wall (left)
6471       element = EL_DC_STEELWALL_1_LEFT;
6472       break;
6473
6474     case 0x1697:        // steel wall (bottom)
6475       element = EL_DC_STEELWALL_1_BOTTOM;
6476       break;
6477
6478     case 0x1698:        // steel wall (right)
6479       element = EL_DC_STEELWALL_1_RIGHT;
6480       break;
6481
6482     case 0x1699:        // steel wall (top)
6483       element = EL_DC_STEELWALL_1_TOP;
6484       break;
6485
6486     case 0x169a:        // steel wall (left/bottom)
6487       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6488       break;
6489
6490     case 0x169b:        // steel wall (right/bottom)
6491       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6492       break;
6493
6494     case 0x169c:        // steel wall (right/top)
6495       element = EL_DC_STEELWALL_1_TOPRIGHT;
6496       break;
6497
6498     case 0x169d:        // steel wall (left/top)
6499       element = EL_DC_STEELWALL_1_TOPLEFT;
6500       break;
6501
6502     case 0x169e:        // steel wall (right/bottom small)
6503       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6504       break;
6505
6506     case 0x169f:        // steel wall (left/bottom small)
6507       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6508       break;
6509
6510     case 0x16a0:        // steel wall (right/top small)
6511       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6512       break;
6513
6514     case 0x16a1:        // steel wall (left/top small)
6515       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6516       break;
6517
6518     case 0x16a2:        // steel wall (left/right)
6519       element = EL_DC_STEELWALL_1_VERTICAL;
6520       break;
6521
6522     case 0x16a3:        // steel wall (top/bottom)
6523       element = EL_DC_STEELWALL_1_HORIZONTAL;
6524       break;
6525
6526     case 0x16a4:        // steel wall 2 (left end)
6527       element = EL_DC_STEELWALL_2_LEFT;
6528       break;
6529
6530     case 0x16a5:        // steel wall 2 (right end)
6531       element = EL_DC_STEELWALL_2_RIGHT;
6532       break;
6533
6534     case 0x16a6:        // steel wall 2 (top end)
6535       element = EL_DC_STEELWALL_2_TOP;
6536       break;
6537
6538     case 0x16a7:        // steel wall 2 (bottom end)
6539       element = EL_DC_STEELWALL_2_BOTTOM;
6540       break;
6541
6542     case 0x16a8:        // steel wall 2 (left/right)
6543       element = EL_DC_STEELWALL_2_HORIZONTAL;
6544       break;
6545
6546     case 0x16a9:        // steel wall 2 (up/down)
6547       element = EL_DC_STEELWALL_2_VERTICAL;
6548       break;
6549
6550     case 0x16aa:        // steel wall 2 (mid)
6551       element = EL_DC_STEELWALL_2_MIDDLE;
6552       break;
6553
6554     case 0x16ab:
6555       element = EL_SIGN_EXCLAMATION;
6556       break;
6557
6558     case 0x16ac:
6559       element = EL_SIGN_RADIOACTIVITY;
6560       break;
6561
6562     case 0x16ad:
6563       element = EL_SIGN_STOP;
6564       break;
6565
6566     case 0x16ae:
6567       element = EL_SIGN_WHEELCHAIR;
6568       break;
6569
6570     case 0x16af:
6571       element = EL_SIGN_PARKING;
6572       break;
6573
6574     case 0x16b0:
6575       element = EL_SIGN_NO_ENTRY;
6576       break;
6577
6578     case 0x16b1:
6579       element = EL_SIGN_HEART;
6580       break;
6581
6582     case 0x16b2:
6583       element = EL_SIGN_GIVE_WAY;
6584       break;
6585
6586     case 0x16b3:
6587       element = EL_SIGN_ENTRY_FORBIDDEN;
6588       break;
6589
6590     case 0x16b4:
6591       element = EL_SIGN_EMERGENCY_EXIT;
6592       break;
6593
6594     case 0x16b5:
6595       element = EL_SIGN_YIN_YANG;
6596       break;
6597
6598     case 0x16b6:
6599       element = EL_WALL_EMERALD;
6600       break;
6601
6602     case 0x16b7:
6603       element = EL_WALL_DIAMOND;
6604       break;
6605
6606     case 0x16b8:
6607       element = EL_WALL_PEARL;
6608       break;
6609
6610     case 0x16b9:
6611       element = EL_WALL_CRYSTAL;
6612       break;
6613
6614     case 0x16ba:
6615       element = EL_INVISIBLE_WALL;
6616       break;
6617
6618     case 0x16bb:
6619       element = EL_INVISIBLE_STEELWALL;
6620       break;
6621
6622       // 0x16bc - 0x16cb:
6623       // EL_INVISIBLE_SAND
6624
6625     case 0x16cc:
6626       element = EL_LIGHT_SWITCH;
6627       break;
6628
6629     case 0x16cd:
6630       element = EL_ENVELOPE_1;
6631       break;
6632
6633     default:
6634       if (element >= 0x0117 && element <= 0x036e)       // (?)
6635         element = EL_DIAMOND;
6636       else if (element >= 0x042d && element <= 0x0684)  // (?)
6637         element = EL_EMERALD;
6638       else if (element >= 0x157c && element <= 0x158b)
6639         element = EL_SAND;
6640       else if (element >= 0x1590 && element <= 0x159f)
6641         element = EL_DC_LANDMINE;
6642       else if (element >= 0x16bc && element <= 0x16cb)
6643         element = EL_INVISIBLE_SAND;
6644       else
6645       {
6646         Warn("unknown Diamond Caves element 0x%04x", element);
6647
6648         element = EL_UNKNOWN;
6649       }
6650       break;
6651   }
6652
6653   return getMappedElement(element);
6654 }
6655
6656 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6657 {
6658   byte header[DC_LEVEL_HEADER_SIZE];
6659   int envelope_size;
6660   int envelope_header_pos = 62;
6661   int envelope_content_pos = 94;
6662   int level_name_pos = 251;
6663   int level_author_pos = 292;
6664   int envelope_header_len;
6665   int envelope_content_len;
6666   int level_name_len;
6667   int level_author_len;
6668   int fieldx, fieldy;
6669   int num_yamyam_contents;
6670   int i, x, y;
6671
6672   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6673
6674   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6675   {
6676     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6677
6678     header[i * 2 + 0] = header_word >> 8;
6679     header[i * 2 + 1] = header_word & 0xff;
6680   }
6681
6682   // read some values from level header to check level decoding integrity
6683   fieldx = header[6] | (header[7] << 8);
6684   fieldy = header[8] | (header[9] << 8);
6685   num_yamyam_contents = header[60] | (header[61] << 8);
6686
6687   // do some simple sanity checks to ensure that level was correctly decoded
6688   if (fieldx < 1 || fieldx > 256 ||
6689       fieldy < 1 || fieldy > 256 ||
6690       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6691   {
6692     level->no_valid_file = TRUE;
6693
6694     Warn("cannot decode level from stream -- using empty level");
6695
6696     return;
6697   }
6698
6699   // maximum envelope header size is 31 bytes
6700   envelope_header_len   = header[envelope_header_pos];
6701   // maximum envelope content size is 110 (156?) bytes
6702   envelope_content_len  = header[envelope_content_pos];
6703
6704   // maximum level title size is 40 bytes
6705   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6706   // maximum level author size is 30 (51?) bytes
6707   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6708
6709   envelope_size = 0;
6710
6711   for (i = 0; i < envelope_header_len; i++)
6712     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6713       level->envelope[0].text[envelope_size++] =
6714         header[envelope_header_pos + 1 + i];
6715
6716   if (envelope_header_len > 0 && envelope_content_len > 0)
6717   {
6718     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6719       level->envelope[0].text[envelope_size++] = '\n';
6720     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6721       level->envelope[0].text[envelope_size++] = '\n';
6722   }
6723
6724   for (i = 0; i < envelope_content_len; i++)
6725     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6726       level->envelope[0].text[envelope_size++] =
6727         header[envelope_content_pos + 1 + i];
6728
6729   level->envelope[0].text[envelope_size] = '\0';
6730
6731   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6732   level->envelope[0].ysize = 10;
6733   level->envelope[0].autowrap = TRUE;
6734   level->envelope[0].centered = TRUE;
6735
6736   for (i = 0; i < level_name_len; i++)
6737     level->name[i] = header[level_name_pos + 1 + i];
6738   level->name[level_name_len] = '\0';
6739
6740   for (i = 0; i < level_author_len; i++)
6741     level->author[i] = header[level_author_pos + 1 + i];
6742   level->author[level_author_len] = '\0';
6743
6744   num_yamyam_contents = header[60] | (header[61] << 8);
6745   level->num_yamyam_contents =
6746     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6747
6748   for (i = 0; i < num_yamyam_contents; i++)
6749   {
6750     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6751     {
6752       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6753       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6754
6755       if (i < MAX_ELEMENT_CONTENTS)
6756         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6757     }
6758   }
6759
6760   fieldx = header[6] | (header[7] << 8);
6761   fieldy = header[8] | (header[9] << 8);
6762   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6763   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6764
6765   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6766   {
6767     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6768     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6769
6770     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6771       level->field[x][y] = getMappedElement_DC(element_dc);
6772   }
6773
6774   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6775   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6776   level->field[x][y] = EL_PLAYER_1;
6777
6778   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6779   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6780   level->field[x][y] = EL_PLAYER_2;
6781
6782   level->gems_needed            = header[18] | (header[19] << 8);
6783
6784   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6785   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6786   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6787   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6788   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6789   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6790   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6791   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6792   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6793   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6794   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6795   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6796
6797   level->time                   = header[44] | (header[45] << 8);
6798
6799   level->amoeba_speed           = header[46] | (header[47] << 8);
6800   level->time_light             = header[48] | (header[49] << 8);
6801   level->time_timegate          = header[50] | (header[51] << 8);
6802   level->time_wheel             = header[52] | (header[53] << 8);
6803   level->time_magic_wall        = header[54] | (header[55] << 8);
6804   level->extra_time             = header[56] | (header[57] << 8);
6805   level->shield_normal_time     = header[58] | (header[59] << 8);
6806
6807   // shield and extra time elements do not have a score
6808   level->score[SC_SHIELD]       = 0;
6809   level->extra_time_score       = 0;
6810
6811   // set time for normal and deadly shields to the same value
6812   level->shield_deadly_time     = level->shield_normal_time;
6813
6814   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6815   // can slip down from flat walls, like normal walls and steel walls
6816   level->em_slippery_gems = TRUE;
6817
6818   // time score is counted for each 10 seconds left in Diamond Caves levels
6819   level->time_score_base = 10;
6820 }
6821
6822 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6823                                      struct LevelFileInfo *level_file_info,
6824                                      boolean level_info_only)
6825 {
6826   char *filename = level_file_info->filename;
6827   File *file;
6828   int num_magic_bytes = 8;
6829   char magic_bytes[num_magic_bytes + 1];
6830   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6831
6832   if (!(file = openFile(filename, MODE_READ)))
6833   {
6834     level->no_valid_file = TRUE;
6835
6836     if (!level_info_only)
6837       Warn("cannot read level '%s' -- using empty level", filename);
6838
6839     return;
6840   }
6841
6842   // fseek(file, 0x0000, SEEK_SET);
6843
6844   if (level_file_info->packed)
6845   {
6846     // read "magic bytes" from start of file
6847     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6848       magic_bytes[0] = '\0';
6849
6850     // check "magic bytes" for correct file format
6851     if (!strPrefix(magic_bytes, "DC2"))
6852     {
6853       level->no_valid_file = TRUE;
6854
6855       Warn("unknown DC level file '%s' -- using empty level", filename);
6856
6857       return;
6858     }
6859
6860     if (strPrefix(magic_bytes, "DC2Win95") ||
6861         strPrefix(magic_bytes, "DC2Win98"))
6862     {
6863       int position_first_level = 0x00fa;
6864       int extra_bytes = 4;
6865       int skip_bytes;
6866
6867       // advance file stream to first level inside the level package
6868       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6869
6870       // each block of level data is followed by block of non-level data
6871       num_levels_to_skip *= 2;
6872
6873       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6874       while (num_levels_to_skip >= 0)
6875       {
6876         // advance file stream to next level inside the level package
6877         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6878         {
6879           level->no_valid_file = TRUE;
6880
6881           Warn("cannot fseek in file '%s' -- using empty level", filename);
6882
6883           return;
6884         }
6885
6886         // skip apparently unused extra bytes following each level
6887         ReadUnusedBytesFromFile(file, extra_bytes);
6888
6889         // read size of next level in level package
6890         skip_bytes = getFile32BitLE(file);
6891
6892         num_levels_to_skip--;
6893       }
6894     }
6895     else
6896     {
6897       level->no_valid_file = TRUE;
6898
6899       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6900
6901       return;
6902     }
6903   }
6904
6905   LoadLevelFromFileStream_DC(file, level);
6906
6907   closeFile(file);
6908 }
6909
6910
6911 // ----------------------------------------------------------------------------
6912 // functions for loading SB level
6913 // ----------------------------------------------------------------------------
6914
6915 int getMappedElement_SB(int element_ascii, boolean use_ces)
6916 {
6917   static struct
6918   {
6919     int ascii;
6920     int sb;
6921     int ce;
6922   }
6923   sb_element_mapping[] =
6924   {
6925     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6926     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6927     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6928     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6929     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6930     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6931     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6932     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6933
6934     { 0,   -1,                      -1          },
6935   };
6936
6937   int i;
6938
6939   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6940     if (element_ascii == sb_element_mapping[i].ascii)
6941       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6942
6943   return EL_UNDEFINED;
6944 }
6945
6946 static void SetLevelSettings_SB(struct LevelInfo *level)
6947 {
6948   // time settings
6949   level->time = 0;
6950   level->use_step_counter = TRUE;
6951
6952   // score settings
6953   level->score[SC_TIME_BONUS] = 0;
6954   level->time_score_base = 1;
6955   level->rate_time_over_score = TRUE;
6956
6957   // game settings
6958   level->auto_exit_sokoban = TRUE;
6959 }
6960
6961 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6962                                      struct LevelFileInfo *level_file_info,
6963                                      boolean level_info_only)
6964 {
6965   char *filename = level_file_info->filename;
6966   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6967   char last_comment[MAX_LINE_LEN];
6968   char level_name[MAX_LINE_LEN];
6969   char *line_ptr;
6970   File *file;
6971   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6972   boolean read_continued_line = FALSE;
6973   boolean reading_playfield = FALSE;
6974   boolean got_valid_playfield_line = FALSE;
6975   boolean invalid_playfield_char = FALSE;
6976   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6977   int file_level_nr = 0;
6978   int x = 0, y = 0;             // initialized to make compilers happy
6979
6980   last_comment[0] = '\0';
6981   level_name[0] = '\0';
6982
6983   if (!(file = openFile(filename, MODE_READ)))
6984   {
6985     level->no_valid_file = TRUE;
6986
6987     if (!level_info_only)
6988       Warn("cannot read level '%s' -- using empty level", filename);
6989
6990     return;
6991   }
6992
6993   while (!checkEndOfFile(file))
6994   {
6995     // level successfully read, but next level may follow here
6996     if (!got_valid_playfield_line && reading_playfield)
6997     {
6998       // read playfield from single level file -- skip remaining file
6999       if (!level_file_info->packed)
7000         break;
7001
7002       if (file_level_nr >= num_levels_to_skip)
7003         break;
7004
7005       file_level_nr++;
7006
7007       last_comment[0] = '\0';
7008       level_name[0] = '\0';
7009
7010       reading_playfield = FALSE;
7011     }
7012
7013     got_valid_playfield_line = FALSE;
7014
7015     // read next line of input file
7016     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7017       break;
7018
7019     // cut trailing line break (this can be newline and/or carriage return)
7020     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7021       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7022         *line_ptr = '\0';
7023
7024     // copy raw input line for later use (mainly debugging output)
7025     strcpy(line_raw, line);
7026
7027     if (read_continued_line)
7028     {
7029       // append new line to existing line, if there is enough space
7030       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7031         strcat(previous_line, line_ptr);
7032
7033       strcpy(line, previous_line);      // copy storage buffer to line
7034
7035       read_continued_line = FALSE;
7036     }
7037
7038     // if the last character is '\', continue at next line
7039     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7040     {
7041       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7042       strcpy(previous_line, line);      // copy line to storage buffer
7043
7044       read_continued_line = TRUE;
7045
7046       continue;
7047     }
7048
7049     // skip empty lines
7050     if (line[0] == '\0')
7051       continue;
7052
7053     // extract comment text from comment line
7054     if (line[0] == ';')
7055     {
7056       for (line_ptr = line; *line_ptr; line_ptr++)
7057         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7058           break;
7059
7060       strcpy(last_comment, line_ptr);
7061
7062       continue;
7063     }
7064
7065     // extract level title text from line containing level title
7066     if (line[0] == '\'')
7067     {
7068       strcpy(level_name, &line[1]);
7069
7070       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7071         level_name[strlen(level_name) - 1] = '\0';
7072
7073       continue;
7074     }
7075
7076     // skip lines containing only spaces (or empty lines)
7077     for (line_ptr = line; *line_ptr; line_ptr++)
7078       if (*line_ptr != ' ')
7079         break;
7080     if (*line_ptr == '\0')
7081       continue;
7082
7083     // at this point, we have found a line containing part of a playfield
7084
7085     got_valid_playfield_line = TRUE;
7086
7087     if (!reading_playfield)
7088     {
7089       reading_playfield = TRUE;
7090       invalid_playfield_char = FALSE;
7091
7092       for (x = 0; x < MAX_LEV_FIELDX; x++)
7093         for (y = 0; y < MAX_LEV_FIELDY; y++)
7094           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7095
7096       level->fieldx = 0;
7097       level->fieldy = 0;
7098
7099       // start with topmost tile row
7100       y = 0;
7101     }
7102
7103     // skip playfield line if larger row than allowed
7104     if (y >= MAX_LEV_FIELDY)
7105       continue;
7106
7107     // start with leftmost tile column
7108     x = 0;
7109
7110     // read playfield elements from line
7111     for (line_ptr = line; *line_ptr; line_ptr++)
7112     {
7113       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7114
7115       // stop parsing playfield line if larger column than allowed
7116       if (x >= MAX_LEV_FIELDX)
7117         break;
7118
7119       if (mapped_sb_element == EL_UNDEFINED)
7120       {
7121         invalid_playfield_char = TRUE;
7122
7123         break;
7124       }
7125
7126       level->field[x][y] = mapped_sb_element;
7127
7128       // continue with next tile column
7129       x++;
7130
7131       level->fieldx = MAX(x, level->fieldx);
7132     }
7133
7134     if (invalid_playfield_char)
7135     {
7136       // if first playfield line, treat invalid lines as comment lines
7137       if (y == 0)
7138         reading_playfield = FALSE;
7139
7140       continue;
7141     }
7142
7143     // continue with next tile row
7144     y++;
7145   }
7146
7147   closeFile(file);
7148
7149   level->fieldy = y;
7150
7151   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7152   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7153
7154   if (!reading_playfield)
7155   {
7156     level->no_valid_file = TRUE;
7157
7158     Warn("cannot read level '%s' -- using empty level", filename);
7159
7160     return;
7161   }
7162
7163   if (*level_name != '\0')
7164   {
7165     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7166     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7167   }
7168   else if (*last_comment != '\0')
7169   {
7170     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7171     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7172   }
7173   else
7174   {
7175     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7176   }
7177
7178   // set all empty fields beyond the border walls to invisible steel wall
7179   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7180   {
7181     if ((x == 0 || x == level->fieldx - 1 ||
7182          y == 0 || y == level->fieldy - 1) &&
7183         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7184       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7185                      level->field, level->fieldx, level->fieldy);
7186   }
7187
7188   // set special level settings for Sokoban levels
7189   SetLevelSettings_SB(level);
7190
7191   if (load_xsb_to_ces)
7192   {
7193     // special global settings can now be set in level template
7194     level->use_custom_template = TRUE;
7195   }
7196 }
7197
7198
7199 // -------------------------------------------------------------------------
7200 // functions for handling native levels
7201 // -------------------------------------------------------------------------
7202
7203 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7204                                      struct LevelFileInfo *level_file_info,
7205                                      boolean level_info_only)
7206 {
7207   int pos = 0;
7208
7209   // determine position of requested level inside level package
7210   if (level_file_info->packed)
7211     pos = level_file_info->nr - leveldir_current->first_level;
7212
7213   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7214     level->no_valid_file = TRUE;
7215 }
7216
7217 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7218                                      struct LevelFileInfo *level_file_info,
7219                                      boolean level_info_only)
7220 {
7221   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7222     level->no_valid_file = TRUE;
7223 }
7224
7225 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7226                                      struct LevelFileInfo *level_file_info,
7227                                      boolean level_info_only)
7228 {
7229   int pos = 0;
7230
7231   // determine position of requested level inside level package
7232   if (level_file_info->packed)
7233     pos = level_file_info->nr - leveldir_current->first_level;
7234
7235   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7236     level->no_valid_file = TRUE;
7237 }
7238
7239 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7240                                      struct LevelFileInfo *level_file_info,
7241                                      boolean level_info_only)
7242 {
7243   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7244     level->no_valid_file = TRUE;
7245 }
7246
7247 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7248 {
7249   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7250     CopyNativeLevel_RND_to_BD(level);
7251   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7252     CopyNativeLevel_RND_to_EM(level);
7253   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7254     CopyNativeLevel_RND_to_SP(level);
7255   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7256     CopyNativeLevel_RND_to_MM(level);
7257 }
7258
7259 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7260 {
7261   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7262     CopyNativeLevel_BD_to_RND(level);
7263   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7264     CopyNativeLevel_EM_to_RND(level);
7265   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7266     CopyNativeLevel_SP_to_RND(level);
7267   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7268     CopyNativeLevel_MM_to_RND(level);
7269 }
7270
7271 void SaveNativeLevel(struct LevelInfo *level)
7272 {
7273   // saving native level files only supported for some game engines
7274   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7275       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7276     return;
7277
7278   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7279                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7280   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7281   char *filename = getLevelFilenameFromBasename(basename);
7282
7283   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7284     return;
7285
7286   boolean success = FALSE;
7287
7288   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7289   {
7290     CopyNativeLevel_RND_to_BD(level);
7291     // CopyNativeTape_RND_to_BD(level);
7292
7293     success = SaveNativeLevel_BD(filename);
7294   }
7295   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7296   {
7297     CopyNativeLevel_RND_to_SP(level);
7298     CopyNativeTape_RND_to_SP(level);
7299
7300     success = SaveNativeLevel_SP(filename);
7301   }
7302
7303   if (success)
7304     Request("Native level file saved!", REQ_CONFIRM);
7305   else
7306     Request("Failed to save native level file!", REQ_CONFIRM);
7307 }
7308
7309
7310 // ----------------------------------------------------------------------------
7311 // functions for loading generic level
7312 // ----------------------------------------------------------------------------
7313
7314 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7315                                   struct LevelFileInfo *level_file_info,
7316                                   boolean level_info_only)
7317 {
7318   // always start with reliable default values
7319   setLevelInfoToDefaults(level, level_info_only, TRUE);
7320
7321   switch (level_file_info->type)
7322   {
7323     case LEVEL_FILE_TYPE_RND:
7324       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7325       break;
7326
7327     case LEVEL_FILE_TYPE_BD:
7328       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7329       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7330       break;
7331
7332     case LEVEL_FILE_TYPE_EM:
7333       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7334       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7335       break;
7336
7337     case LEVEL_FILE_TYPE_SP:
7338       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7339       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7340       break;
7341
7342     case LEVEL_FILE_TYPE_MM:
7343       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7344       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7345       break;
7346
7347     case LEVEL_FILE_TYPE_DC:
7348       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7349       break;
7350
7351     case LEVEL_FILE_TYPE_SB:
7352       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7353       break;
7354
7355     default:
7356       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7357       break;
7358   }
7359
7360   // if level file is invalid, restore level structure to default values
7361   if (level->no_valid_file)
7362     setLevelInfoToDefaults(level, level_info_only, FALSE);
7363
7364   if (check_special_flags("use_native_bd_game_engine"))
7365     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7366
7367   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7368     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7369
7370   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7371     CopyNativeLevel_Native_to_RND(level);
7372 }
7373
7374 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7375 {
7376   static struct LevelFileInfo level_file_info;
7377
7378   // always start with reliable default values
7379   setFileInfoToDefaults(&level_file_info);
7380
7381   level_file_info.nr = 0;                       // unknown level number
7382   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7383
7384   setString(&level_file_info.filename, filename);
7385
7386   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7387 }
7388
7389 static void LoadLevel_InitVersion(struct LevelInfo *level)
7390 {
7391   int i, j;
7392
7393   if (leveldir_current == NULL)         // only when dumping level
7394     return;
7395
7396   // all engine modifications also valid for levels which use latest engine
7397   if (level->game_version < VERSION_IDENT(3,2,0,5))
7398   {
7399     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7400     level->time_score_base = 10;
7401   }
7402
7403   if (leveldir_current->latest_engine)
7404   {
7405     // ---------- use latest game engine --------------------------------------
7406
7407     /* For all levels which are forced to use the latest game engine version
7408        (normally all but user contributed, private and undefined levels), set
7409        the game engine version to the actual version; this allows for actual
7410        corrections in the game engine to take effect for existing, converted
7411        levels (from "classic" or other existing games) to make the emulation
7412        of the corresponding game more accurate, while (hopefully) not breaking
7413        existing levels created from other players. */
7414
7415     level->game_version = GAME_VERSION_ACTUAL;
7416
7417     /* Set special EM style gems behaviour: EM style gems slip down from
7418        normal, steel and growing wall. As this is a more fundamental change,
7419        it seems better to set the default behaviour to "off" (as it is more
7420        natural) and make it configurable in the level editor (as a property
7421        of gem style elements). Already existing converted levels (neither
7422        private nor contributed levels) are changed to the new behaviour. */
7423
7424     if (level->file_version < FILE_VERSION_2_0)
7425       level->em_slippery_gems = TRUE;
7426
7427     return;
7428   }
7429
7430   // ---------- use game engine the level was created with --------------------
7431
7432   /* For all levels which are not forced to use the latest game engine
7433      version (normally user contributed, private and undefined levels),
7434      use the version of the game engine the levels were created for.
7435
7436      Since 2.0.1, the game engine version is now directly stored
7437      in the level file (chunk "VERS"), so there is no need anymore
7438      to set the game version from the file version (except for old,
7439      pre-2.0 levels, where the game version is still taken from the
7440      file format version used to store the level -- see above). */
7441
7442   // player was faster than enemies in 1.0.0 and before
7443   if (level->file_version == FILE_VERSION_1_0)
7444     for (i = 0; i < MAX_PLAYERS; i++)
7445       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7446
7447   // default behaviour for EM style gems was "slippery" only in 2.0.1
7448   if (level->game_version == VERSION_IDENT(2,0,1,0))
7449     level->em_slippery_gems = TRUE;
7450
7451   // springs could be pushed over pits before (pre-release version) 2.2.0
7452   if (level->game_version < VERSION_IDENT(2,2,0,0))
7453     level->use_spring_bug = TRUE;
7454
7455   if (level->game_version < VERSION_IDENT(3,2,0,5))
7456   {
7457     // time orb caused limited time in endless time levels before 3.2.0-5
7458     level->use_time_orb_bug = TRUE;
7459
7460     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7461     level->block_snap_field = FALSE;
7462
7463     // extra time score was same value as time left score before 3.2.0-5
7464     level->extra_time_score = level->score[SC_TIME_BONUS];
7465   }
7466
7467   if (level->game_version < VERSION_IDENT(3,2,0,7))
7468   {
7469     // default behaviour for snapping was "not continuous" before 3.2.0-7
7470     level->continuous_snapping = FALSE;
7471   }
7472
7473   // only few elements were able to actively move into acid before 3.1.0
7474   // trigger settings did not exist before 3.1.0; set to default "any"
7475   if (level->game_version < VERSION_IDENT(3,1,0,0))
7476   {
7477     // correct "can move into acid" settings (all zero in old levels)
7478
7479     level->can_move_into_acid_bits = 0; // nothing can move into acid
7480     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7481
7482     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7483     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7484     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7485     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7486
7487     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7488       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7489
7490     // correct trigger settings (stored as zero == "none" in old levels)
7491
7492     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7493     {
7494       int element = EL_CUSTOM_START + i;
7495       struct ElementInfo *ei = &element_info[element];
7496
7497       for (j = 0; j < ei->num_change_pages; j++)
7498       {
7499         struct ElementChangeInfo *change = &ei->change_page[j];
7500
7501         change->trigger_player = CH_PLAYER_ANY;
7502         change->trigger_page = CH_PAGE_ANY;
7503       }
7504     }
7505   }
7506
7507   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7508   {
7509     int element = EL_CUSTOM_256;
7510     struct ElementInfo *ei = &element_info[element];
7511     struct ElementChangeInfo *change = &ei->change_page[0];
7512
7513     /* This is needed to fix a problem that was caused by a bugfix in function
7514        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7515        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7516        not replace walkable elements, but instead just placed the player on it,
7517        without placing the Sokoban field under the player). Unfortunately, this
7518        breaks "Snake Bite" style levels when the snake is halfway through a door
7519        that just closes (the snake head is still alive and can be moved in this
7520        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7521        player (without Sokoban element) which then gets killed as designed). */
7522
7523     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7524          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7525         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7526       change->target_element = EL_PLAYER_1;
7527   }
7528
7529   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7530   if (level->game_version < VERSION_IDENT(3,2,5,0))
7531   {
7532     /* This is needed to fix a problem that was caused by a bugfix in function
7533        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7534        corrects the behaviour when a custom element changes to another custom
7535        element with a higher element number that has change actions defined.
7536        Normally, only one change per frame is allowed for custom elements.
7537        Therefore, it is checked if a custom element already changed in the
7538        current frame; if it did, subsequent changes are suppressed.
7539        Unfortunately, this is only checked for element changes, but not for
7540        change actions, which are still executed. As the function above loops
7541        through all custom elements from lower to higher, an element change
7542        resulting in a lower CE number won't be checked again, while a target
7543        element with a higher number will also be checked, and potential change
7544        actions will get executed for this CE, too (which is wrong), while
7545        further changes are ignored (which is correct). As this bugfix breaks
7546        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7547        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7548        behaviour for existing levels and tapes that make use of this bug */
7549
7550     level->use_action_after_change_bug = TRUE;
7551   }
7552
7553   // not centering level after relocating player was default only in 3.2.3
7554   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7555     level->shifted_relocation = TRUE;
7556
7557   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7558   if (level->game_version < VERSION_IDENT(3,2,6,0))
7559     level->em_explodes_by_fire = TRUE;
7560
7561   // levels were solved by the first player entering an exit up to 4.1.0.0
7562   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7563     level->solved_by_one_player = TRUE;
7564
7565   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7566   if (level->game_version < VERSION_IDENT(4,1,1,1))
7567     level->use_life_bugs = TRUE;
7568
7569   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7570   if (level->game_version < VERSION_IDENT(4,1,1,1))
7571     level->sb_objects_needed = FALSE;
7572
7573   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7574   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7575     level->finish_dig_collect = FALSE;
7576
7577   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7578   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7579     level->keep_walkable_ce = TRUE;
7580 }
7581
7582 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7583 {
7584   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7585   int x, y;
7586
7587   // check if this level is (not) a Sokoban level
7588   for (y = 0; y < level->fieldy; y++)
7589     for (x = 0; x < level->fieldx; x++)
7590       if (!IS_SB_ELEMENT(Tile[x][y]))
7591         is_sokoban_level = FALSE;
7592
7593   if (is_sokoban_level)
7594   {
7595     // set special level settings for Sokoban levels
7596     SetLevelSettings_SB(level);
7597   }
7598 }
7599
7600 static void LoadLevel_InitSettings(struct LevelInfo *level)
7601 {
7602   // adjust level settings for (non-native) Sokoban-style levels
7603   LoadLevel_InitSettings_SB(level);
7604
7605   // rename levels with title "nameless level" or if renaming is forced
7606   if (leveldir_current->empty_level_name != NULL &&
7607       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7608        leveldir_current->force_level_name))
7609     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7610              leveldir_current->empty_level_name, level_nr);
7611 }
7612
7613 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7614 {
7615   int i, x, y;
7616
7617   // map elements that have changed in newer versions
7618   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7619                                                     level->game_version);
7620   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7621     for (x = 0; x < 3; x++)
7622       for (y = 0; y < 3; y++)
7623         level->yamyam_content[i].e[x][y] =
7624           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7625                                     level->game_version);
7626
7627 }
7628
7629 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7630 {
7631   int i, j;
7632
7633   // map custom element change events that have changed in newer versions
7634   // (these following values were accidentally changed in version 3.0.1)
7635   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7636   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7637   {
7638     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7639     {
7640       int element = EL_CUSTOM_START + i;
7641
7642       // order of checking and copying events to be mapped is important
7643       // (do not change the start and end value -- they are constant)
7644       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7645       {
7646         if (HAS_CHANGE_EVENT(element, j - 2))
7647         {
7648           SET_CHANGE_EVENT(element, j - 2, FALSE);
7649           SET_CHANGE_EVENT(element, j, TRUE);
7650         }
7651       }
7652
7653       // order of checking and copying events to be mapped is important
7654       // (do not change the start and end value -- they are constant)
7655       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7656       {
7657         if (HAS_CHANGE_EVENT(element, j - 1))
7658         {
7659           SET_CHANGE_EVENT(element, j - 1, FALSE);
7660           SET_CHANGE_EVENT(element, j, TRUE);
7661         }
7662       }
7663     }
7664   }
7665
7666   // initialize "can_change" field for old levels with only one change page
7667   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7668   {
7669     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7670     {
7671       int element = EL_CUSTOM_START + i;
7672
7673       if (CAN_CHANGE(element))
7674         element_info[element].change->can_change = TRUE;
7675     }
7676   }
7677
7678   // correct custom element values (for old levels without these options)
7679   if (level->game_version < VERSION_IDENT(3,1,1,0))
7680   {
7681     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7682     {
7683       int element = EL_CUSTOM_START + i;
7684       struct ElementInfo *ei = &element_info[element];
7685
7686       if (ei->access_direction == MV_NO_DIRECTION)
7687         ei->access_direction = MV_ALL_DIRECTIONS;
7688     }
7689   }
7690
7691   // correct custom element values (fix invalid values for all versions)
7692   if (1)
7693   {
7694     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7695     {
7696       int element = EL_CUSTOM_START + i;
7697       struct ElementInfo *ei = &element_info[element];
7698
7699       for (j = 0; j < ei->num_change_pages; j++)
7700       {
7701         struct ElementChangeInfo *change = &ei->change_page[j];
7702
7703         if (change->trigger_player == CH_PLAYER_NONE)
7704           change->trigger_player = CH_PLAYER_ANY;
7705
7706         if (change->trigger_side == CH_SIDE_NONE)
7707           change->trigger_side = CH_SIDE_ANY;
7708       }
7709     }
7710   }
7711
7712   // initialize "can_explode" field for old levels which did not store this
7713   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7714   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7715   {
7716     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7717     {
7718       int element = EL_CUSTOM_START + i;
7719
7720       if (EXPLODES_1X1_OLD(element))
7721         element_info[element].explosion_type = EXPLODES_1X1;
7722
7723       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7724                                              EXPLODES_SMASHED(element) ||
7725                                              EXPLODES_IMPACT(element)));
7726     }
7727   }
7728
7729   // correct previously hard-coded move delay values for maze runner style
7730   if (level->game_version < VERSION_IDENT(3,1,1,0))
7731   {
7732     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7733     {
7734       int element = EL_CUSTOM_START + i;
7735
7736       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7737       {
7738         // previously hard-coded and therefore ignored
7739         element_info[element].move_delay_fixed = 9;
7740         element_info[element].move_delay_random = 0;
7741       }
7742     }
7743   }
7744
7745   // set some other uninitialized values of custom elements in older levels
7746   if (level->game_version < VERSION_IDENT(3,1,0,0))
7747   {
7748     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7749     {
7750       int element = EL_CUSTOM_START + i;
7751
7752       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7753
7754       element_info[element].explosion_delay = 17;
7755       element_info[element].ignition_delay = 8;
7756     }
7757   }
7758
7759   // set mouse click change events to work for left/middle/right mouse button
7760   if (level->game_version < VERSION_IDENT(4,2,3,0))
7761   {
7762     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7763     {
7764       int element = EL_CUSTOM_START + i;
7765       struct ElementInfo *ei = &element_info[element];
7766
7767       for (j = 0; j < ei->num_change_pages; j++)
7768       {
7769         struct ElementChangeInfo *change = &ei->change_page[j];
7770
7771         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7772             change->has_event[CE_PRESSED_BY_MOUSE] ||
7773             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7774             change->has_event[CE_MOUSE_PRESSED_ON_X])
7775           change->trigger_side = CH_SIDE_ANY;
7776       }
7777     }
7778   }
7779 }
7780
7781 static void LoadLevel_InitElements(struct LevelInfo *level)
7782 {
7783   LoadLevel_InitStandardElements(level);
7784
7785   if (level->file_has_custom_elements)
7786     LoadLevel_InitCustomElements(level);
7787
7788   // initialize element properties for level editor etc.
7789   InitElementPropertiesEngine(level->game_version);
7790   InitElementPropertiesGfxElement();
7791 }
7792
7793 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7794 {
7795   int x, y;
7796
7797   // map elements that have changed in newer versions
7798   for (y = 0; y < level->fieldy; y++)
7799     for (x = 0; x < level->fieldx; x++)
7800       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7801                                                      level->game_version);
7802
7803   // clear unused playfield data (nicer if level gets resized in editor)
7804   for (x = 0; x < MAX_LEV_FIELDX; x++)
7805     for (y = 0; y < MAX_LEV_FIELDY; y++)
7806       if (x >= level->fieldx || y >= level->fieldy)
7807         level->field[x][y] = EL_EMPTY;
7808
7809   // copy elements to runtime playfield array
7810   for (x = 0; x < MAX_LEV_FIELDX; x++)
7811     for (y = 0; y < MAX_LEV_FIELDY; y++)
7812       Tile[x][y] = level->field[x][y];
7813
7814   // initialize level size variables for faster access
7815   lev_fieldx = level->fieldx;
7816   lev_fieldy = level->fieldy;
7817
7818   // determine border element for this level
7819   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7820     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7821   else
7822     SetBorderElement();
7823 }
7824
7825 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7826 {
7827   struct LevelFileInfo *level_file_info = &level->file_info;
7828
7829   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7830     CopyNativeLevel_RND_to_Native(level);
7831 }
7832
7833 static void LoadLevelTemplate_LoadAndInit(void)
7834 {
7835   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7836
7837   LoadLevel_InitVersion(&level_template);
7838   LoadLevel_InitElements(&level_template);
7839   LoadLevel_InitSettings(&level_template);
7840
7841   ActivateLevelTemplate();
7842 }
7843
7844 void LoadLevelTemplate(int nr)
7845 {
7846   if (!fileExists(getGlobalLevelTemplateFilename()))
7847   {
7848     Warn("no level template found for this level");
7849
7850     return;
7851   }
7852
7853   setLevelFileInfo(&level_template.file_info, nr);
7854
7855   LoadLevelTemplate_LoadAndInit();
7856 }
7857
7858 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7859 {
7860   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7861
7862   LoadLevelTemplate_LoadAndInit();
7863 }
7864
7865 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7866 {
7867   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7868
7869   if (level.use_custom_template)
7870   {
7871     if (network_level != NULL)
7872       LoadNetworkLevelTemplate(network_level);
7873     else
7874       LoadLevelTemplate(-1);
7875   }
7876
7877   LoadLevel_InitVersion(&level);
7878   LoadLevel_InitElements(&level);
7879   LoadLevel_InitPlayfield(&level);
7880   LoadLevel_InitSettings(&level);
7881
7882   LoadLevel_InitNativeEngines(&level);
7883 }
7884
7885 void LoadLevel(int nr)
7886 {
7887   SetLevelSetInfo(leveldir_current->identifier, nr);
7888
7889   setLevelFileInfo(&level.file_info, nr);
7890
7891   LoadLevel_LoadAndInit(NULL);
7892 }
7893
7894 void LoadLevelInfoOnly(int nr)
7895 {
7896   setLevelFileInfo(&level.file_info, nr);
7897
7898   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7899 }
7900
7901 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7902 {
7903   SetLevelSetInfo(network_level->leveldir_identifier,
7904                   network_level->file_info.nr);
7905
7906   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7907
7908   LoadLevel_LoadAndInit(network_level);
7909 }
7910
7911 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7912 {
7913   int chunk_size = 0;
7914
7915   chunk_size += putFileVersion(file, level->file_version);
7916   chunk_size += putFileVersion(file, level->game_version);
7917
7918   return chunk_size;
7919 }
7920
7921 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7922 {
7923   int chunk_size = 0;
7924
7925   chunk_size += putFile16BitBE(file, level->creation_date.year);
7926   chunk_size += putFile8Bit(file,    level->creation_date.month);
7927   chunk_size += putFile8Bit(file,    level->creation_date.day);
7928
7929   return chunk_size;
7930 }
7931
7932 #if ENABLE_HISTORIC_CHUNKS
7933 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7934 {
7935   int i, x, y;
7936
7937   putFile8Bit(file, level->fieldx);
7938   putFile8Bit(file, level->fieldy);
7939
7940   putFile16BitBE(file, level->time);
7941   putFile16BitBE(file, level->gems_needed);
7942
7943   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7944     putFile8Bit(file, level->name[i]);
7945
7946   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7947     putFile8Bit(file, level->score[i]);
7948
7949   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7950     for (y = 0; y < 3; y++)
7951       for (x = 0; x < 3; x++)
7952         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7953                            level->yamyam_content[i].e[x][y]));
7954   putFile8Bit(file, level->amoeba_speed);
7955   putFile8Bit(file, level->time_magic_wall);
7956   putFile8Bit(file, level->time_wheel);
7957   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7958                      level->amoeba_content));
7959   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7960   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7961   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7962   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7963
7964   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7965
7966   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7967   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7968   putFile32BitBE(file, level->can_move_into_acid_bits);
7969   putFile8Bit(file, level->dont_collide_with_bits);
7970
7971   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7972   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7973
7974   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7975   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7976   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7977
7978   putFile8Bit(file, level->game_engine_type);
7979
7980   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7981 }
7982 #endif
7983
7984 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7985 {
7986   int chunk_size = 0;
7987   int i;
7988
7989   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7990     chunk_size += putFile8Bit(file, level->name[i]);
7991
7992   return chunk_size;
7993 }
7994
7995 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7996 {
7997   int chunk_size = 0;
7998   int i;
7999
8000   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8001     chunk_size += putFile8Bit(file, level->author[i]);
8002
8003   return chunk_size;
8004 }
8005
8006 #if ENABLE_HISTORIC_CHUNKS
8007 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8008 {
8009   int chunk_size = 0;
8010   int x, y;
8011
8012   for (y = 0; y < level->fieldy; y++)
8013     for (x = 0; x < level->fieldx; x++)
8014       if (level->encoding_16bit_field)
8015         chunk_size += putFile16BitBE(file, level->field[x][y]);
8016       else
8017         chunk_size += putFile8Bit(file, level->field[x][y]);
8018
8019   return chunk_size;
8020 }
8021 #endif
8022
8023 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8024 {
8025   int chunk_size = 0;
8026   int x, y;
8027
8028   for (y = 0; y < level->fieldy; y++) 
8029     for (x = 0; x < level->fieldx; x++) 
8030       chunk_size += putFile16BitBE(file, level->field[x][y]);
8031
8032   return chunk_size;
8033 }
8034
8035 #if ENABLE_HISTORIC_CHUNKS
8036 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8037 {
8038   int i, x, y;
8039
8040   putFile8Bit(file, EL_YAMYAM);
8041   putFile8Bit(file, level->num_yamyam_contents);
8042   putFile8Bit(file, 0);
8043   putFile8Bit(file, 0);
8044
8045   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8046     for (y = 0; y < 3; y++)
8047       for (x = 0; x < 3; x++)
8048         if (level->encoding_16bit_field)
8049           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8050         else
8051           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8052 }
8053 #endif
8054
8055 #if ENABLE_HISTORIC_CHUNKS
8056 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8057 {
8058   int i, x, y;
8059   int num_contents, content_xsize, content_ysize;
8060   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8061
8062   if (element == EL_YAMYAM)
8063   {
8064     num_contents = level->num_yamyam_contents;
8065     content_xsize = 3;
8066     content_ysize = 3;
8067
8068     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8069       for (y = 0; y < 3; y++)
8070         for (x = 0; x < 3; x++)
8071           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8072   }
8073   else if (element == EL_BD_AMOEBA)
8074   {
8075     num_contents = 1;
8076     content_xsize = 1;
8077     content_ysize = 1;
8078
8079     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8080       for (y = 0; y < 3; y++)
8081         for (x = 0; x < 3; x++)
8082           content_array[i][x][y] = EL_EMPTY;
8083     content_array[0][0][0] = level->amoeba_content;
8084   }
8085   else
8086   {
8087     // chunk header already written -- write empty chunk data
8088     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8089
8090     Warn("cannot save content for element '%d'", element);
8091
8092     return;
8093   }
8094
8095   putFile16BitBE(file, element);
8096   putFile8Bit(file, num_contents);
8097   putFile8Bit(file, content_xsize);
8098   putFile8Bit(file, content_ysize);
8099
8100   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8101
8102   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8103     for (y = 0; y < 3; y++)
8104       for (x = 0; x < 3; x++)
8105         putFile16BitBE(file, content_array[i][x][y]);
8106 }
8107 #endif
8108
8109 #if ENABLE_HISTORIC_CHUNKS
8110 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8111 {
8112   int envelope_nr = element - EL_ENVELOPE_1;
8113   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8114   int chunk_size = 0;
8115   int i;
8116
8117   chunk_size += putFile16BitBE(file, element);
8118   chunk_size += putFile16BitBE(file, envelope_len);
8119   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8120   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8121
8122   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8123   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8124
8125   for (i = 0; i < envelope_len; i++)
8126     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8127
8128   return chunk_size;
8129 }
8130 #endif
8131
8132 #if ENABLE_HISTORIC_CHUNKS
8133 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8134                            int num_changed_custom_elements)
8135 {
8136   int i, check = 0;
8137
8138   putFile16BitBE(file, num_changed_custom_elements);
8139
8140   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8141   {
8142     int element = EL_CUSTOM_START + i;
8143
8144     struct ElementInfo *ei = &element_info[element];
8145
8146     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8147     {
8148       if (check < num_changed_custom_elements)
8149       {
8150         putFile16BitBE(file, element);
8151         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8152       }
8153
8154       check++;
8155     }
8156   }
8157
8158   if (check != num_changed_custom_elements)     // should not happen
8159     Warn("inconsistent number of custom element properties");
8160 }
8161 #endif
8162
8163 #if ENABLE_HISTORIC_CHUNKS
8164 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8165                            int num_changed_custom_elements)
8166 {
8167   int i, check = 0;
8168
8169   putFile16BitBE(file, num_changed_custom_elements);
8170
8171   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8172   {
8173     int element = EL_CUSTOM_START + i;
8174
8175     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8176     {
8177       if (check < num_changed_custom_elements)
8178       {
8179         putFile16BitBE(file, element);
8180         putFile16BitBE(file, element_info[element].change->target_element);
8181       }
8182
8183       check++;
8184     }
8185   }
8186
8187   if (check != num_changed_custom_elements)     // should not happen
8188     Warn("inconsistent number of custom target elements");
8189 }
8190 #endif
8191
8192 #if ENABLE_HISTORIC_CHUNKS
8193 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8194                            int num_changed_custom_elements)
8195 {
8196   int i, j, x, y, check = 0;
8197
8198   putFile16BitBE(file, num_changed_custom_elements);
8199
8200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8201   {
8202     int element = EL_CUSTOM_START + i;
8203     struct ElementInfo *ei = &element_info[element];
8204
8205     if (ei->modified_settings)
8206     {
8207       if (check < num_changed_custom_elements)
8208       {
8209         putFile16BitBE(file, element);
8210
8211         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8212           putFile8Bit(file, ei->description[j]);
8213
8214         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8215
8216         // some free bytes for future properties and padding
8217         WriteUnusedBytesToFile(file, 7);
8218
8219         putFile8Bit(file, ei->use_gfx_element);
8220         putFile16BitBE(file, ei->gfx_element_initial);
8221
8222         putFile8Bit(file, ei->collect_score_initial);
8223         putFile8Bit(file, ei->collect_count_initial);
8224
8225         putFile16BitBE(file, ei->push_delay_fixed);
8226         putFile16BitBE(file, ei->push_delay_random);
8227         putFile16BitBE(file, ei->move_delay_fixed);
8228         putFile16BitBE(file, ei->move_delay_random);
8229
8230         putFile16BitBE(file, ei->move_pattern);
8231         putFile8Bit(file, ei->move_direction_initial);
8232         putFile8Bit(file, ei->move_stepsize);
8233
8234         for (y = 0; y < 3; y++)
8235           for (x = 0; x < 3; x++)
8236             putFile16BitBE(file, ei->content.e[x][y]);
8237
8238         putFile32BitBE(file, ei->change->events);
8239
8240         putFile16BitBE(file, ei->change->target_element);
8241
8242         putFile16BitBE(file, ei->change->delay_fixed);
8243         putFile16BitBE(file, ei->change->delay_random);
8244         putFile16BitBE(file, ei->change->delay_frames);
8245
8246         putFile16BitBE(file, ei->change->initial_trigger_element);
8247
8248         putFile8Bit(file, ei->change->explode);
8249         putFile8Bit(file, ei->change->use_target_content);
8250         putFile8Bit(file, ei->change->only_if_complete);
8251         putFile8Bit(file, ei->change->use_random_replace);
8252
8253         putFile8Bit(file, ei->change->random_percentage);
8254         putFile8Bit(file, ei->change->replace_when);
8255
8256         for (y = 0; y < 3; y++)
8257           for (x = 0; x < 3; x++)
8258             putFile16BitBE(file, ei->change->content.e[x][y]);
8259
8260         putFile8Bit(file, ei->slippery_type);
8261
8262         // some free bytes for future properties and padding
8263         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8264       }
8265
8266       check++;
8267     }
8268   }
8269
8270   if (check != num_changed_custom_elements)     // should not happen
8271     Warn("inconsistent number of custom element properties");
8272 }
8273 #endif
8274
8275 #if ENABLE_HISTORIC_CHUNKS
8276 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8277 {
8278   struct ElementInfo *ei = &element_info[element];
8279   int i, j, x, y;
8280
8281   // ---------- custom element base property values (96 bytes) ----------------
8282
8283   putFile16BitBE(file, element);
8284
8285   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8286     putFile8Bit(file, ei->description[i]);
8287
8288   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8289
8290   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8291
8292   putFile8Bit(file, ei->num_change_pages);
8293
8294   putFile16BitBE(file, ei->ce_value_fixed_initial);
8295   putFile16BitBE(file, ei->ce_value_random_initial);
8296   putFile8Bit(file, ei->use_last_ce_value);
8297
8298   putFile8Bit(file, ei->use_gfx_element);
8299   putFile16BitBE(file, ei->gfx_element_initial);
8300
8301   putFile8Bit(file, ei->collect_score_initial);
8302   putFile8Bit(file, ei->collect_count_initial);
8303
8304   putFile8Bit(file, ei->drop_delay_fixed);
8305   putFile8Bit(file, ei->push_delay_fixed);
8306   putFile8Bit(file, ei->drop_delay_random);
8307   putFile8Bit(file, ei->push_delay_random);
8308   putFile16BitBE(file, ei->move_delay_fixed);
8309   putFile16BitBE(file, ei->move_delay_random);
8310
8311   // bits 0 - 15 of "move_pattern" ...
8312   putFile16BitBE(file, ei->move_pattern & 0xffff);
8313   putFile8Bit(file, ei->move_direction_initial);
8314   putFile8Bit(file, ei->move_stepsize);
8315
8316   putFile8Bit(file, ei->slippery_type);
8317
8318   for (y = 0; y < 3; y++)
8319     for (x = 0; x < 3; x++)
8320       putFile16BitBE(file, ei->content.e[x][y]);
8321
8322   putFile16BitBE(file, ei->move_enter_element);
8323   putFile16BitBE(file, ei->move_leave_element);
8324   putFile8Bit(file, ei->move_leave_type);
8325
8326   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8327   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8328
8329   putFile8Bit(file, ei->access_direction);
8330
8331   putFile8Bit(file, ei->explosion_delay);
8332   putFile8Bit(file, ei->ignition_delay);
8333   putFile8Bit(file, ei->explosion_type);
8334
8335   // some free bytes for future custom property values and padding
8336   WriteUnusedBytesToFile(file, 1);
8337
8338   // ---------- change page property values (48 bytes) ------------------------
8339
8340   for (i = 0; i < ei->num_change_pages; i++)
8341   {
8342     struct ElementChangeInfo *change = &ei->change_page[i];
8343     unsigned int event_bits;
8344
8345     // bits 0 - 31 of "has_event[]" ...
8346     event_bits = 0;
8347     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8348       if (change->has_event[j])
8349         event_bits |= (1u << j);
8350     putFile32BitBE(file, event_bits);
8351
8352     putFile16BitBE(file, change->target_element);
8353
8354     putFile16BitBE(file, change->delay_fixed);
8355     putFile16BitBE(file, change->delay_random);
8356     putFile16BitBE(file, change->delay_frames);
8357
8358     putFile16BitBE(file, change->initial_trigger_element);
8359
8360     putFile8Bit(file, change->explode);
8361     putFile8Bit(file, change->use_target_content);
8362     putFile8Bit(file, change->only_if_complete);
8363     putFile8Bit(file, change->use_random_replace);
8364
8365     putFile8Bit(file, change->random_percentage);
8366     putFile8Bit(file, change->replace_when);
8367
8368     for (y = 0; y < 3; y++)
8369       for (x = 0; x < 3; x++)
8370         putFile16BitBE(file, change->target_content.e[x][y]);
8371
8372     putFile8Bit(file, change->can_change);
8373
8374     putFile8Bit(file, change->trigger_side);
8375
8376     putFile8Bit(file, change->trigger_player);
8377     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8378                        log_2(change->trigger_page)));
8379
8380     putFile8Bit(file, change->has_action);
8381     putFile8Bit(file, change->action_type);
8382     putFile8Bit(file, change->action_mode);
8383     putFile16BitBE(file, change->action_arg);
8384
8385     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8386     event_bits = 0;
8387     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8388       if (change->has_event[j])
8389         event_bits |= (1u << (j - 32));
8390     putFile8Bit(file, event_bits);
8391   }
8392 }
8393 #endif
8394
8395 #if ENABLE_HISTORIC_CHUNKS
8396 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8397 {
8398   struct ElementInfo *ei = &element_info[element];
8399   struct ElementGroupInfo *group = ei->group;
8400   int i;
8401
8402   putFile16BitBE(file, element);
8403
8404   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8405     putFile8Bit(file, ei->description[i]);
8406
8407   putFile8Bit(file, group->num_elements);
8408
8409   putFile8Bit(file, ei->use_gfx_element);
8410   putFile16BitBE(file, ei->gfx_element_initial);
8411
8412   putFile8Bit(file, group->choice_mode);
8413
8414   // some free bytes for future values and padding
8415   WriteUnusedBytesToFile(file, 3);
8416
8417   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8418     putFile16BitBE(file, group->element[i]);
8419 }
8420 #endif
8421
8422 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8423                                 boolean write_element)
8424 {
8425   int save_type = entry->save_type;
8426   int data_type = entry->data_type;
8427   int conf_type = entry->conf_type;
8428   int byte_mask = conf_type & CONF_MASK_BYTES;
8429   int element = entry->element;
8430   int default_value = entry->default_value;
8431   int num_bytes = 0;
8432   boolean modified = FALSE;
8433
8434   if (byte_mask != CONF_MASK_MULTI_BYTES)
8435   {
8436     void *value_ptr = entry->value;
8437     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8438                  *(int *)value_ptr);
8439
8440     // check if any settings have been modified before saving them
8441     if (value != default_value)
8442       modified = TRUE;
8443
8444     // do not save if explicitly told or if unmodified default settings
8445     if ((save_type == SAVE_CONF_NEVER) ||
8446         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8447       return 0;
8448
8449     if (write_element)
8450       num_bytes += putFile16BitBE(file, element);
8451
8452     num_bytes += putFile8Bit(file, conf_type);
8453     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8454                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8455                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8456                   0);
8457   }
8458   else if (data_type == TYPE_STRING)
8459   {
8460     char *default_string = entry->default_string;
8461     char *string = (char *)(entry->value);
8462     int string_length = strlen(string);
8463     int i;
8464
8465     // check if any settings have been modified before saving them
8466     if (!strEqual(string, default_string))
8467       modified = TRUE;
8468
8469     // do not save if explicitly told or if unmodified default settings
8470     if ((save_type == SAVE_CONF_NEVER) ||
8471         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8472       return 0;
8473
8474     if (write_element)
8475       num_bytes += putFile16BitBE(file, element);
8476
8477     num_bytes += putFile8Bit(file, conf_type);
8478     num_bytes += putFile16BitBE(file, string_length);
8479
8480     for (i = 0; i < string_length; i++)
8481       num_bytes += putFile8Bit(file, string[i]);
8482   }
8483   else if (data_type == TYPE_ELEMENT_LIST)
8484   {
8485     int *element_array = (int *)(entry->value);
8486     int num_elements = *(int *)(entry->num_entities);
8487     int i;
8488
8489     // check if any settings have been modified before saving them
8490     for (i = 0; i < num_elements; i++)
8491       if (element_array[i] != default_value)
8492         modified = TRUE;
8493
8494     // do not save if explicitly told or if unmodified default settings
8495     if ((save_type == SAVE_CONF_NEVER) ||
8496         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8497       return 0;
8498
8499     if (write_element)
8500       num_bytes += putFile16BitBE(file, element);
8501
8502     num_bytes += putFile8Bit(file, conf_type);
8503     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8504
8505     for (i = 0; i < num_elements; i++)
8506       num_bytes += putFile16BitBE(file, element_array[i]);
8507   }
8508   else if (data_type == TYPE_CONTENT_LIST)
8509   {
8510     struct Content *content = (struct Content *)(entry->value);
8511     int num_contents = *(int *)(entry->num_entities);
8512     int i, x, y;
8513
8514     // check if any settings have been modified before saving them
8515     for (i = 0; i < num_contents; i++)
8516       for (y = 0; y < 3; y++)
8517         for (x = 0; x < 3; x++)
8518           if (content[i].e[x][y] != default_value)
8519             modified = TRUE;
8520
8521     // do not save if explicitly told or if unmodified default settings
8522     if ((save_type == SAVE_CONF_NEVER) ||
8523         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8524       return 0;
8525
8526     if (write_element)
8527       num_bytes += putFile16BitBE(file, element);
8528
8529     num_bytes += putFile8Bit(file, conf_type);
8530     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8531
8532     for (i = 0; i < num_contents; i++)
8533       for (y = 0; y < 3; y++)
8534         for (x = 0; x < 3; x++)
8535           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8536   }
8537
8538   return num_bytes;
8539 }
8540
8541 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8542 {
8543   int chunk_size = 0;
8544   int i;
8545
8546   li = *level;          // copy level data into temporary buffer
8547
8548   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8549     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8550
8551   return chunk_size;
8552 }
8553
8554 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8555 {
8556   int chunk_size = 0;
8557   int i;
8558
8559   li = *level;          // copy level data into temporary buffer
8560
8561   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8562     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8563
8564   return chunk_size;
8565 }
8566
8567 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8568 {
8569   int envelope_nr = element - EL_ENVELOPE_1;
8570   int chunk_size = 0;
8571   int i;
8572
8573   chunk_size += putFile16BitBE(file, element);
8574
8575   // copy envelope data into temporary buffer
8576   xx_envelope = level->envelope[envelope_nr];
8577
8578   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8579     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8580
8581   return chunk_size;
8582 }
8583
8584 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8585 {
8586   struct ElementInfo *ei = &element_info[element];
8587   int chunk_size = 0;
8588   int i, j;
8589
8590   chunk_size += putFile16BitBE(file, element);
8591
8592   xx_ei = *ei;          // copy element data into temporary buffer
8593
8594   // set default description string for this specific element
8595   strcpy(xx_default_description, getDefaultElementDescription(ei));
8596
8597   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8598     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8599
8600   for (i = 0; i < ei->num_change_pages; i++)
8601   {
8602     struct ElementChangeInfo *change = &ei->change_page[i];
8603
8604     xx_current_change_page = i;
8605
8606     xx_change = *change;        // copy change data into temporary buffer
8607
8608     resetEventBits();
8609     setEventBitsFromEventFlags(change);
8610
8611     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8612       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8613                                          FALSE);
8614   }
8615
8616   return chunk_size;
8617 }
8618
8619 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8620 {
8621   struct ElementInfo *ei = &element_info[element];
8622   struct ElementGroupInfo *group = ei->group;
8623   int chunk_size = 0;
8624   int i;
8625
8626   chunk_size += putFile16BitBE(file, element);
8627
8628   xx_ei = *ei;          // copy element data into temporary buffer
8629   xx_group = *group;    // copy group data into temporary buffer
8630
8631   // set default description string for this specific element
8632   strcpy(xx_default_description, getDefaultElementDescription(ei));
8633
8634   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8635     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8636
8637   return chunk_size;
8638 }
8639
8640 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8641 {
8642   struct ElementInfo *ei = &element_info[element];
8643   int chunk_size = 0;
8644   int i;
8645
8646   chunk_size += putFile16BitBE(file, element);
8647
8648   xx_ei = *ei;          // copy element data into temporary buffer
8649
8650   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8651     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8652
8653   return chunk_size;
8654 }
8655
8656 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8657                                   boolean save_as_template)
8658 {
8659   int chunk_size;
8660   int i;
8661   FILE *file;
8662
8663   if (!(file = fopen(filename, MODE_WRITE)))
8664   {
8665     Warn("cannot save level file '%s'", filename);
8666
8667     return;
8668   }
8669
8670   level->file_version = FILE_VERSION_ACTUAL;
8671   level->game_version = GAME_VERSION_ACTUAL;
8672
8673   level->creation_date = getCurrentDate();
8674
8675   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8676   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8677
8678   chunk_size = SaveLevel_VERS(NULL, level);
8679   putFileChunkBE(file, "VERS", chunk_size);
8680   SaveLevel_VERS(file, level);
8681
8682   chunk_size = SaveLevel_DATE(NULL, level);
8683   putFileChunkBE(file, "DATE", chunk_size);
8684   SaveLevel_DATE(file, level);
8685
8686   chunk_size = SaveLevel_NAME(NULL, level);
8687   putFileChunkBE(file, "NAME", chunk_size);
8688   SaveLevel_NAME(file, level);
8689
8690   chunk_size = SaveLevel_AUTH(NULL, level);
8691   putFileChunkBE(file, "AUTH", chunk_size);
8692   SaveLevel_AUTH(file, level);
8693
8694   chunk_size = SaveLevel_INFO(NULL, level);
8695   putFileChunkBE(file, "INFO", chunk_size);
8696   SaveLevel_INFO(file, level);
8697
8698   chunk_size = SaveLevel_BODY(NULL, level);
8699   putFileChunkBE(file, "BODY", chunk_size);
8700   SaveLevel_BODY(file, level);
8701
8702   chunk_size = SaveLevel_ELEM(NULL, level);
8703   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8704   {
8705     putFileChunkBE(file, "ELEM", chunk_size);
8706     SaveLevel_ELEM(file, level);
8707   }
8708
8709   for (i = 0; i < NUM_ENVELOPES; i++)
8710   {
8711     int element = EL_ENVELOPE_1 + i;
8712
8713     chunk_size = SaveLevel_NOTE(NULL, level, element);
8714     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8715     {
8716       putFileChunkBE(file, "NOTE", chunk_size);
8717       SaveLevel_NOTE(file, level, element);
8718     }
8719   }
8720
8721   // if not using template level, check for non-default custom/group elements
8722   if (!level->use_custom_template || save_as_template)
8723   {
8724     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8725     {
8726       int element = EL_CUSTOM_START + i;
8727
8728       chunk_size = SaveLevel_CUSX(NULL, level, element);
8729       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8730       {
8731         putFileChunkBE(file, "CUSX", chunk_size);
8732         SaveLevel_CUSX(file, level, element);
8733       }
8734     }
8735
8736     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8737     {
8738       int element = EL_GROUP_START + i;
8739
8740       chunk_size = SaveLevel_GRPX(NULL, level, element);
8741       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8742       {
8743         putFileChunkBE(file, "GRPX", chunk_size);
8744         SaveLevel_GRPX(file, level, element);
8745       }
8746     }
8747
8748     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8749     {
8750       int element = GET_EMPTY_ELEMENT(i);
8751
8752       chunk_size = SaveLevel_EMPX(NULL, level, element);
8753       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8754       {
8755         putFileChunkBE(file, "EMPX", chunk_size);
8756         SaveLevel_EMPX(file, level, element);
8757       }
8758     }
8759   }
8760
8761   fclose(file);
8762
8763   SetFilePermissions(filename, PERMS_PRIVATE);
8764 }
8765
8766 void SaveLevel(int nr)
8767 {
8768   char *filename = getDefaultLevelFilename(nr);
8769
8770   SaveLevelFromFilename(&level, filename, FALSE);
8771 }
8772
8773 void SaveLevelTemplate(void)
8774 {
8775   char *filename = getLocalLevelTemplateFilename();
8776
8777   SaveLevelFromFilename(&level, filename, TRUE);
8778 }
8779
8780 boolean SaveLevelChecked(int nr)
8781 {
8782   char *filename = getDefaultLevelFilename(nr);
8783   boolean new_level = !fileExists(filename);
8784   boolean level_saved = FALSE;
8785
8786   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8787   {
8788     SaveLevel(nr);
8789
8790     if (new_level)
8791       Request("Level saved!", REQ_CONFIRM);
8792
8793     level_saved = TRUE;
8794   }
8795
8796   return level_saved;
8797 }
8798
8799 void DumpLevel(struct LevelInfo *level)
8800 {
8801   if (level->no_level_file || level->no_valid_file)
8802   {
8803     Warn("cannot dump -- no valid level file found");
8804
8805     return;
8806   }
8807
8808   PrintLine("-", 79);
8809   Print("Level xxx (file version %08d, game version %08d)\n",
8810         level->file_version, level->game_version);
8811   PrintLine("-", 79);
8812
8813   Print("Level author: '%s'\n", level->author);
8814   Print("Level title:  '%s'\n", level->name);
8815   Print("\n");
8816   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8817   Print("\n");
8818   Print("Level time:  %d seconds\n", level->time);
8819   Print("Gems needed: %d\n", level->gems_needed);
8820   Print("\n");
8821   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8822   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8823   Print("Time for light:      %d seconds\n", level->time_light);
8824   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8825   Print("\n");
8826   Print("Amoeba speed: %d\n", level->amoeba_speed);
8827   Print("\n");
8828
8829   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8830   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8831   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8832   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8833   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8834   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8835
8836   if (options.debug)
8837   {
8838     int i, j;
8839
8840     for (i = 0; i < NUM_ENVELOPES; i++)
8841     {
8842       char *text = level->envelope[i].text;
8843       int text_len = strlen(text);
8844       boolean has_text = FALSE;
8845
8846       for (j = 0; j < text_len; j++)
8847         if (text[j] != ' ' && text[j] != '\n')
8848           has_text = TRUE;
8849
8850       if (has_text)
8851       {
8852         Print("\n");
8853         Print("Envelope %d:\n'%s'\n", i + 1, text);
8854       }
8855     }
8856   }
8857
8858   PrintLine("-", 79);
8859 }
8860
8861 void DumpLevels(void)
8862 {
8863   static LevelDirTree *dumplevel_leveldir = NULL;
8864
8865   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8866                                                  global.dumplevel_leveldir);
8867
8868   if (dumplevel_leveldir == NULL)
8869     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8870
8871   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8872       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8873     Fail("no such level number: %d", global.dumplevel_level_nr);
8874
8875   leveldir_current = dumplevel_leveldir;
8876
8877   LoadLevel(global.dumplevel_level_nr);
8878   DumpLevel(&level);
8879
8880   CloseAllAndExit(0);
8881 }
8882
8883
8884 // ============================================================================
8885 // tape file functions
8886 // ============================================================================
8887
8888 static void setTapeInfoToDefaults(void)
8889 {
8890   int i;
8891
8892   // always start with reliable default values (empty tape)
8893   TapeErase();
8894
8895   // default values (also for pre-1.2 tapes) with only the first player
8896   tape.player_participates[0] = TRUE;
8897   for (i = 1; i < MAX_PLAYERS; i++)
8898     tape.player_participates[i] = FALSE;
8899
8900   // at least one (default: the first) player participates in every tape
8901   tape.num_participating_players = 1;
8902
8903   tape.property_bits = TAPE_PROPERTY_NONE;
8904
8905   tape.level_nr = level_nr;
8906   tape.counter = 0;
8907   tape.changed = FALSE;
8908   tape.solved = FALSE;
8909
8910   tape.recording = FALSE;
8911   tape.playing = FALSE;
8912   tape.pausing = FALSE;
8913
8914   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8915   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8916
8917   tape.no_info_chunk = TRUE;
8918   tape.no_valid_file = FALSE;
8919 }
8920
8921 static int getTapePosSize(struct TapeInfo *tape)
8922 {
8923   int tape_pos_size = 0;
8924
8925   if (tape->use_key_actions)
8926     tape_pos_size += tape->num_participating_players;
8927
8928   if (tape->use_mouse_actions)
8929     tape_pos_size += 3;         // x and y position and mouse button mask
8930
8931   tape_pos_size += 1;           // tape action delay value
8932
8933   return tape_pos_size;
8934 }
8935
8936 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8937 {
8938   tape->use_key_actions = FALSE;
8939   tape->use_mouse_actions = FALSE;
8940
8941   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8942     tape->use_key_actions = TRUE;
8943
8944   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8945     tape->use_mouse_actions = TRUE;
8946 }
8947
8948 static int getTapeActionValue(struct TapeInfo *tape)
8949 {
8950   return (tape->use_key_actions &&
8951           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8952           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8953           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8954           TAPE_ACTIONS_DEFAULT);
8955 }
8956
8957 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8958 {
8959   tape->file_version = getFileVersion(file);
8960   tape->game_version = getFileVersion(file);
8961
8962   return chunk_size;
8963 }
8964
8965 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8966 {
8967   int i;
8968
8969   tape->random_seed = getFile32BitBE(file);
8970   tape->date        = getFile32BitBE(file);
8971   tape->length      = getFile32BitBE(file);
8972
8973   // read header fields that are new since version 1.2
8974   if (tape->file_version >= FILE_VERSION_1_2)
8975   {
8976     byte store_participating_players = getFile8Bit(file);
8977     int engine_version;
8978
8979     // since version 1.2, tapes store which players participate in the tape
8980     tape->num_participating_players = 0;
8981     for (i = 0; i < MAX_PLAYERS; i++)
8982     {
8983       tape->player_participates[i] = FALSE;
8984
8985       if (store_participating_players & (1 << i))
8986       {
8987         tape->player_participates[i] = TRUE;
8988         tape->num_participating_players++;
8989       }
8990     }
8991
8992     setTapeActionFlags(tape, getFile8Bit(file));
8993
8994     tape->property_bits = getFile8Bit(file);
8995     tape->solved = getFile8Bit(file);
8996
8997     engine_version = getFileVersion(file);
8998     if (engine_version > 0)
8999       tape->engine_version = engine_version;
9000     else
9001       tape->engine_version = tape->game_version;
9002   }
9003
9004   return chunk_size;
9005 }
9006
9007 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9008 {
9009   tape->scr_fieldx = getFile8Bit(file);
9010   tape->scr_fieldy = getFile8Bit(file);
9011
9012   return chunk_size;
9013 }
9014
9015 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9016 {
9017   char *level_identifier = NULL;
9018   int level_identifier_size;
9019   int i;
9020
9021   tape->no_info_chunk = FALSE;
9022
9023   level_identifier_size = getFile16BitBE(file);
9024
9025   level_identifier = checked_malloc(level_identifier_size);
9026
9027   for (i = 0; i < level_identifier_size; i++)
9028     level_identifier[i] = getFile8Bit(file);
9029
9030   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9031   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9032
9033   checked_free(level_identifier);
9034
9035   tape->level_nr = getFile16BitBE(file);
9036
9037   chunk_size = 2 + level_identifier_size + 2;
9038
9039   return chunk_size;
9040 }
9041
9042 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9043 {
9044   int i, j;
9045   int tape_pos_size = getTapePosSize(tape);
9046   int chunk_size_expected = tape_pos_size * tape->length;
9047
9048   if (chunk_size_expected != chunk_size)
9049   {
9050     ReadUnusedBytesFromFile(file, chunk_size);
9051     return chunk_size_expected;
9052   }
9053
9054   for (i = 0; i < tape->length; i++)
9055   {
9056     if (i >= MAX_TAPE_LEN)
9057     {
9058       Warn("tape truncated -- size exceeds maximum tape size %d",
9059             MAX_TAPE_LEN);
9060
9061       // tape too large; read and ignore remaining tape data from this chunk
9062       for (;i < tape->length; i++)
9063         ReadUnusedBytesFromFile(file, tape_pos_size);
9064
9065       break;
9066     }
9067
9068     if (tape->use_key_actions)
9069     {
9070       for (j = 0; j < MAX_PLAYERS; j++)
9071       {
9072         tape->pos[i].action[j] = MV_NONE;
9073
9074         if (tape->player_participates[j])
9075           tape->pos[i].action[j] = getFile8Bit(file);
9076       }
9077     }
9078
9079     if (tape->use_mouse_actions)
9080     {
9081       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9082       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9083       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9084     }
9085
9086     tape->pos[i].delay = getFile8Bit(file);
9087
9088     if (tape->file_version == FILE_VERSION_1_0)
9089     {
9090       // eliminate possible diagonal moves in old tapes
9091       // this is only for backward compatibility
9092
9093       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9094       byte action = tape->pos[i].action[0];
9095       int k, num_moves = 0;
9096
9097       for (k = 0; k < 4; k++)
9098       {
9099         if (action & joy_dir[k])
9100         {
9101           tape->pos[i + num_moves].action[0] = joy_dir[k];
9102           if (num_moves > 0)
9103             tape->pos[i + num_moves].delay = 0;
9104           num_moves++;
9105         }
9106       }
9107
9108       if (num_moves > 1)
9109       {
9110         num_moves--;
9111         i += num_moves;
9112         tape->length += num_moves;
9113       }
9114     }
9115     else if (tape->file_version < FILE_VERSION_2_0)
9116     {
9117       // convert pre-2.0 tapes to new tape format
9118
9119       if (tape->pos[i].delay > 1)
9120       {
9121         // action part
9122         tape->pos[i + 1] = tape->pos[i];
9123         tape->pos[i + 1].delay = 1;
9124
9125         // delay part
9126         for (j = 0; j < MAX_PLAYERS; j++)
9127           tape->pos[i].action[j] = MV_NONE;
9128         tape->pos[i].delay--;
9129
9130         i++;
9131         tape->length++;
9132       }
9133     }
9134
9135     if (checkEndOfFile(file))
9136       break;
9137   }
9138
9139   if (i != tape->length)
9140     chunk_size = tape_pos_size * i;
9141
9142   return chunk_size;
9143 }
9144
9145 static void LoadTape_SokobanSolution(char *filename)
9146 {
9147   File *file;
9148   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9149
9150   if (!(file = openFile(filename, MODE_READ)))
9151   {
9152     tape.no_valid_file = TRUE;
9153
9154     return;
9155   }
9156
9157   while (!checkEndOfFile(file))
9158   {
9159     unsigned char c = getByteFromFile(file);
9160
9161     if (checkEndOfFile(file))
9162       break;
9163
9164     switch (c)
9165     {
9166       case 'u':
9167       case 'U':
9168         tape.pos[tape.length].action[0] = MV_UP;
9169         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9170         tape.length++;
9171         break;
9172
9173       case 'd':
9174       case 'D':
9175         tape.pos[tape.length].action[0] = MV_DOWN;
9176         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9177         tape.length++;
9178         break;
9179
9180       case 'l':
9181       case 'L':
9182         tape.pos[tape.length].action[0] = MV_LEFT;
9183         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9184         tape.length++;
9185         break;
9186
9187       case 'r':
9188       case 'R':
9189         tape.pos[tape.length].action[0] = MV_RIGHT;
9190         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9191         tape.length++;
9192         break;
9193
9194       case '\n':
9195       case '\r':
9196       case '\t':
9197       case ' ':
9198         // ignore white-space characters
9199         break;
9200
9201       default:
9202         tape.no_valid_file = TRUE;
9203
9204         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9205
9206         break;
9207     }
9208   }
9209
9210   closeFile(file);
9211
9212   if (tape.no_valid_file)
9213     return;
9214
9215   tape.length_frames  = GetTapeLengthFrames();
9216   tape.length_seconds = GetTapeLengthSeconds();
9217 }
9218
9219 void LoadTapeFromFilename(char *filename)
9220 {
9221   char cookie[MAX_LINE_LEN];
9222   char chunk_name[CHUNK_ID_LEN + 1];
9223   File *file;
9224   int chunk_size;
9225
9226   // always start with reliable default values
9227   setTapeInfoToDefaults();
9228
9229   if (strSuffix(filename, ".sln"))
9230   {
9231     LoadTape_SokobanSolution(filename);
9232
9233     return;
9234   }
9235
9236   if (!(file = openFile(filename, MODE_READ)))
9237   {
9238     tape.no_valid_file = TRUE;
9239
9240     return;
9241   }
9242
9243   getFileChunkBE(file, chunk_name, NULL);
9244   if (strEqual(chunk_name, "RND1"))
9245   {
9246     getFile32BitBE(file);               // not used
9247
9248     getFileChunkBE(file, chunk_name, NULL);
9249     if (!strEqual(chunk_name, "TAPE"))
9250     {
9251       tape.no_valid_file = TRUE;
9252
9253       Warn("unknown format of tape file '%s'", filename);
9254
9255       closeFile(file);
9256
9257       return;
9258     }
9259   }
9260   else  // check for pre-2.0 file format with cookie string
9261   {
9262     strcpy(cookie, chunk_name);
9263     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9264       cookie[4] = '\0';
9265     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9266       cookie[strlen(cookie) - 1] = '\0';
9267
9268     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9269     {
9270       tape.no_valid_file = TRUE;
9271
9272       Warn("unknown format of tape file '%s'", filename);
9273
9274       closeFile(file);
9275
9276       return;
9277     }
9278
9279     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9280     {
9281       tape.no_valid_file = TRUE;
9282
9283       Warn("unsupported version of tape file '%s'", filename);
9284
9285       closeFile(file);
9286
9287       return;
9288     }
9289
9290     // pre-2.0 tape files have no game version, so use file version here
9291     tape.game_version = tape.file_version;
9292   }
9293
9294   if (tape.file_version < FILE_VERSION_1_2)
9295   {
9296     // tape files from versions before 1.2.0 without chunk structure
9297     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9298     LoadTape_BODY(file, 2 * tape.length,      &tape);
9299   }
9300   else
9301   {
9302     static struct
9303     {
9304       char *name;
9305       int size;
9306       int (*loader)(File *, int, struct TapeInfo *);
9307     }
9308     chunk_info[] =
9309     {
9310       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9311       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9312       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9313       { "INFO", -1,                     LoadTape_INFO },
9314       { "BODY", -1,                     LoadTape_BODY },
9315       {  NULL,  0,                      NULL }
9316     };
9317
9318     while (getFileChunkBE(file, chunk_name, &chunk_size))
9319     {
9320       int i = 0;
9321
9322       while (chunk_info[i].name != NULL &&
9323              !strEqual(chunk_name, chunk_info[i].name))
9324         i++;
9325
9326       if (chunk_info[i].name == NULL)
9327       {
9328         Warn("unknown chunk '%s' in tape file '%s'",
9329               chunk_name, filename);
9330
9331         ReadUnusedBytesFromFile(file, chunk_size);
9332       }
9333       else if (chunk_info[i].size != -1 &&
9334                chunk_info[i].size != chunk_size)
9335       {
9336         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9337               chunk_size, chunk_name, filename);
9338
9339         ReadUnusedBytesFromFile(file, chunk_size);
9340       }
9341       else
9342       {
9343         // call function to load this tape chunk
9344         int chunk_size_expected =
9345           (chunk_info[i].loader)(file, chunk_size, &tape);
9346
9347         // the size of some chunks cannot be checked before reading other
9348         // chunks first (like "HEAD" and "BODY") that contain some header
9349         // information, so check them here
9350         if (chunk_size_expected != chunk_size)
9351         {
9352           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9353                 chunk_size, chunk_name, filename);
9354         }
9355       }
9356     }
9357   }
9358
9359   closeFile(file);
9360
9361   tape.length_frames  = GetTapeLengthFrames();
9362   tape.length_seconds = GetTapeLengthSeconds();
9363
9364 #if 0
9365   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9366         tape.file_version);
9367   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9368         tape.game_version);
9369   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9370         tape.engine_version);
9371 #endif
9372 }
9373
9374 void LoadTape(int nr)
9375 {
9376   char *filename = getTapeFilename(nr);
9377
9378   LoadTapeFromFilename(filename);
9379 }
9380
9381 void LoadSolutionTape(int nr)
9382 {
9383   char *filename = getSolutionTapeFilename(nr);
9384
9385   LoadTapeFromFilename(filename);
9386
9387   if (TAPE_IS_EMPTY(tape))
9388   {
9389     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9390         level.native_bd_level->replay != NULL)
9391       CopyNativeTape_BD_to_RND(&level);
9392     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9393         level.native_sp_level->demo.is_available)
9394       CopyNativeTape_SP_to_RND(&level);
9395   }
9396 }
9397
9398 void LoadScoreTape(char *score_tape_basename, int nr)
9399 {
9400   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9401
9402   LoadTapeFromFilename(filename);
9403 }
9404
9405 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9406 {
9407   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9408
9409   LoadTapeFromFilename(filename);
9410 }
9411
9412 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9413 {
9414   // chunk required for team mode tapes with non-default screen size
9415   return (tape->num_participating_players > 1 &&
9416           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9417            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9418 }
9419
9420 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9421 {
9422   putFileVersion(file, tape->file_version);
9423   putFileVersion(file, tape->game_version);
9424 }
9425
9426 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9427 {
9428   int i;
9429   byte store_participating_players = 0;
9430
9431   // set bits for participating players for compact storage
9432   for (i = 0; i < MAX_PLAYERS; i++)
9433     if (tape->player_participates[i])
9434       store_participating_players |= (1 << i);
9435
9436   putFile32BitBE(file, tape->random_seed);
9437   putFile32BitBE(file, tape->date);
9438   putFile32BitBE(file, tape->length);
9439
9440   putFile8Bit(file, store_participating_players);
9441
9442   putFile8Bit(file, getTapeActionValue(tape));
9443
9444   putFile8Bit(file, tape->property_bits);
9445   putFile8Bit(file, tape->solved);
9446
9447   putFileVersion(file, tape->engine_version);
9448 }
9449
9450 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9451 {
9452   putFile8Bit(file, tape->scr_fieldx);
9453   putFile8Bit(file, tape->scr_fieldy);
9454 }
9455
9456 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9457 {
9458   int level_identifier_size = strlen(tape->level_identifier) + 1;
9459   int i;
9460
9461   putFile16BitBE(file, level_identifier_size);
9462
9463   for (i = 0; i < level_identifier_size; i++)
9464     putFile8Bit(file, tape->level_identifier[i]);
9465
9466   putFile16BitBE(file, tape->level_nr);
9467 }
9468
9469 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9470 {
9471   int i, j;
9472
9473   for (i = 0; i < tape->length; i++)
9474   {
9475     if (tape->use_key_actions)
9476     {
9477       for (j = 0; j < MAX_PLAYERS; j++)
9478         if (tape->player_participates[j])
9479           putFile8Bit(file, tape->pos[i].action[j]);
9480     }
9481
9482     if (tape->use_mouse_actions)
9483     {
9484       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9485       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9486       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9487     }
9488
9489     putFile8Bit(file, tape->pos[i].delay);
9490   }
9491 }
9492
9493 void SaveTapeToFilename(char *filename)
9494 {
9495   FILE *file;
9496   int tape_pos_size;
9497   int info_chunk_size;
9498   int body_chunk_size;
9499
9500   if (!(file = fopen(filename, MODE_WRITE)))
9501   {
9502     Warn("cannot save level recording file '%s'", filename);
9503
9504     return;
9505   }
9506
9507   tape_pos_size = getTapePosSize(&tape);
9508
9509   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9510   body_chunk_size = tape_pos_size * tape.length;
9511
9512   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9513   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9514
9515   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9516   SaveTape_VERS(file, &tape);
9517
9518   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9519   SaveTape_HEAD(file, &tape);
9520
9521   if (checkSaveTape_SCRN(&tape))
9522   {
9523     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9524     SaveTape_SCRN(file, &tape);
9525   }
9526
9527   putFileChunkBE(file, "INFO", info_chunk_size);
9528   SaveTape_INFO(file, &tape);
9529
9530   putFileChunkBE(file, "BODY", body_chunk_size);
9531   SaveTape_BODY(file, &tape);
9532
9533   fclose(file);
9534
9535   SetFilePermissions(filename, PERMS_PRIVATE);
9536 }
9537
9538 static void SaveTapeExt(char *filename)
9539 {
9540   int i;
9541
9542   tape.file_version = FILE_VERSION_ACTUAL;
9543   tape.game_version = GAME_VERSION_ACTUAL;
9544
9545   tape.num_participating_players = 0;
9546
9547   // count number of participating players
9548   for (i = 0; i < MAX_PLAYERS; i++)
9549     if (tape.player_participates[i])
9550       tape.num_participating_players++;
9551
9552   SaveTapeToFilename(filename);
9553
9554   tape.changed = FALSE;
9555 }
9556
9557 void SaveTape(int nr)
9558 {
9559   char *filename = getTapeFilename(nr);
9560
9561   InitTapeDirectory(leveldir_current->subdir);
9562
9563   SaveTapeExt(filename);
9564 }
9565
9566 void SaveScoreTape(int nr)
9567 {
9568   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9569
9570   // used instead of "leveldir_current->subdir" (for network games)
9571   InitScoreTapeDirectory(levelset.identifier, nr);
9572
9573   SaveTapeExt(filename);
9574 }
9575
9576 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9577                                   unsigned int req_state_added)
9578 {
9579   char *filename = getTapeFilename(nr);
9580   boolean new_tape = !fileExists(filename);
9581   boolean tape_saved = FALSE;
9582
9583   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9584   {
9585     SaveTape(nr);
9586
9587     if (new_tape)
9588       Request(msg_saved, REQ_CONFIRM | req_state_added);
9589
9590     tape_saved = TRUE;
9591   }
9592
9593   return tape_saved;
9594 }
9595
9596 boolean SaveTapeChecked(int nr)
9597 {
9598   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9599 }
9600
9601 boolean SaveTapeChecked_LevelSolved(int nr)
9602 {
9603   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9604                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9605 }
9606
9607 void DumpTape(struct TapeInfo *tape)
9608 {
9609   int tape_frame_counter;
9610   int i, j;
9611
9612   if (tape->no_valid_file)
9613   {
9614     Warn("cannot dump -- no valid tape file found");
9615
9616     return;
9617   }
9618
9619   PrintLine("-", 79);
9620
9621   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9622         tape->level_nr, tape->file_version, tape->game_version);
9623   Print("                  (effective engine version %08d)\n",
9624         tape->engine_version);
9625   Print("Level series identifier: '%s'\n", tape->level_identifier);
9626
9627   Print("Solution tape: %s\n",
9628         tape->solved ? "yes" :
9629         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9630
9631   Print("Special tape properties: ");
9632   if (tape->property_bits == TAPE_PROPERTY_NONE)
9633     Print("[none]");
9634   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9635     Print("[em_random_bug]");
9636   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9637     Print("[game_speed]");
9638   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9639     Print("[pause]");
9640   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9641     Print("[single_step]");
9642   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9643     Print("[snapshot]");
9644   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9645     Print("[replayed]");
9646   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9647     Print("[tas_keys]");
9648   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9649     Print("[small_graphics]");
9650   Print("\n");
9651
9652   int year2 = tape->date / 10000;
9653   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9654   int month_index_raw = (tape->date / 100) % 100;
9655   int month_index = month_index_raw % 12;       // prevent invalid index
9656   int month = month_index + 1;
9657   int day = tape->date % 100;
9658
9659   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9660
9661   PrintLine("-", 79);
9662
9663   tape_frame_counter = 0;
9664
9665   for (i = 0; i < tape->length; i++)
9666   {
9667     if (i >= MAX_TAPE_LEN)
9668       break;
9669
9670     Print("%04d: ", i);
9671
9672     for (j = 0; j < MAX_PLAYERS; j++)
9673     {
9674       if (tape->player_participates[j])
9675       {
9676         int action = tape->pos[i].action[j];
9677
9678         Print("%d:%02x ", j, action);
9679         Print("[%c%c%c%c|%c%c] - ",
9680               (action & JOY_LEFT ? '<' : ' '),
9681               (action & JOY_RIGHT ? '>' : ' '),
9682               (action & JOY_UP ? '^' : ' '),
9683               (action & JOY_DOWN ? 'v' : ' '),
9684               (action & JOY_BUTTON_1 ? '1' : ' '),
9685               (action & JOY_BUTTON_2 ? '2' : ' '));
9686       }
9687     }
9688
9689     Print("(%03d) ", tape->pos[i].delay);
9690     Print("[%05d]\n", tape_frame_counter);
9691
9692     tape_frame_counter += tape->pos[i].delay;
9693   }
9694
9695   PrintLine("-", 79);
9696 }
9697
9698 void DumpTapes(void)
9699 {
9700   static LevelDirTree *dumptape_leveldir = NULL;
9701
9702   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9703                                                 global.dumptape_leveldir);
9704
9705   if (dumptape_leveldir == NULL)
9706     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9707
9708   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9709       global.dumptape_level_nr > dumptape_leveldir->last_level)
9710     Fail("no such level number: %d", global.dumptape_level_nr);
9711
9712   leveldir_current = dumptape_leveldir;
9713
9714   if (options.mytapes)
9715     LoadTape(global.dumptape_level_nr);
9716   else
9717     LoadSolutionTape(global.dumptape_level_nr);
9718
9719   DumpTape(&tape);
9720
9721   CloseAllAndExit(0);
9722 }
9723
9724
9725 // ============================================================================
9726 // score file functions
9727 // ============================================================================
9728
9729 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9730 {
9731   int i;
9732
9733   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9734   {
9735     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9736     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9737     scores->entry[i].score = 0;
9738     scores->entry[i].time = 0;
9739
9740     scores->entry[i].id = -1;
9741     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9742     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9743     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9744     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9745     strcpy(scores->entry[i].country_code, "??");
9746   }
9747
9748   scores->num_entries = 0;
9749   scores->last_added = -1;
9750   scores->last_added_local = -1;
9751
9752   scores->updated = FALSE;
9753   scores->uploaded = FALSE;
9754   scores->tape_downloaded = FALSE;
9755   scores->force_last_added = FALSE;
9756
9757   // The following values are intentionally not reset here:
9758   // - last_level_nr
9759   // - last_entry_nr
9760   // - next_level_nr
9761   // - continue_playing
9762   // - continue_on_return
9763 }
9764
9765 static void setScoreInfoToDefaults(void)
9766 {
9767   setScoreInfoToDefaultsExt(&scores);
9768 }
9769
9770 static void setServerScoreInfoToDefaults(void)
9771 {
9772   setScoreInfoToDefaultsExt(&server_scores);
9773 }
9774
9775 static void LoadScore_OLD(int nr)
9776 {
9777   int i;
9778   char *filename = getScoreFilename(nr);
9779   char cookie[MAX_LINE_LEN];
9780   char line[MAX_LINE_LEN];
9781   char *line_ptr;
9782   FILE *file;
9783
9784   if (!(file = fopen(filename, MODE_READ)))
9785     return;
9786
9787   // check file identifier
9788   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9789     cookie[0] = '\0';
9790   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9791     cookie[strlen(cookie) - 1] = '\0';
9792
9793   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9794   {
9795     Warn("unknown format of score file '%s'", filename);
9796
9797     fclose(file);
9798
9799     return;
9800   }
9801
9802   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9803   {
9804     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9805       Warn("fscanf() failed; %s", strerror(errno));
9806
9807     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9808       line[0] = '\0';
9809
9810     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9811       line[strlen(line) - 1] = '\0';
9812
9813     for (line_ptr = line; *line_ptr; line_ptr++)
9814     {
9815       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9816       {
9817         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9818         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9819         break;
9820       }
9821     }
9822   }
9823
9824   fclose(file);
9825 }
9826
9827 static void ConvertScore_OLD(void)
9828 {
9829   // only convert score to time for levels that rate playing time over score
9830   if (!level.rate_time_over_score)
9831     return;
9832
9833   // convert old score to playing time for score-less levels (like Supaplex)
9834   int time_final_max = 999;
9835   int i;
9836
9837   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9838   {
9839     int score = scores.entry[i].score;
9840
9841     if (score > 0 && score < time_final_max)
9842       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9843   }
9844 }
9845
9846 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9847 {
9848   scores->file_version = getFileVersion(file);
9849   scores->game_version = getFileVersion(file);
9850
9851   return chunk_size;
9852 }
9853
9854 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9855 {
9856   char *level_identifier = NULL;
9857   int level_identifier_size;
9858   int i;
9859
9860   level_identifier_size = getFile16BitBE(file);
9861
9862   level_identifier = checked_malloc(level_identifier_size);
9863
9864   for (i = 0; i < level_identifier_size; i++)
9865     level_identifier[i] = getFile8Bit(file);
9866
9867   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9868   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9869
9870   checked_free(level_identifier);
9871
9872   scores->level_nr = getFile16BitBE(file);
9873   scores->num_entries = getFile16BitBE(file);
9874
9875   chunk_size = 2 + level_identifier_size + 2 + 2;
9876
9877   return chunk_size;
9878 }
9879
9880 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9881 {
9882   int i, j;
9883
9884   for (i = 0; i < scores->num_entries; i++)
9885   {
9886     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9887       scores->entry[i].name[j] = getFile8Bit(file);
9888
9889     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9890   }
9891
9892   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9893
9894   return chunk_size;
9895 }
9896
9897 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9898 {
9899   int i;
9900
9901   for (i = 0; i < scores->num_entries; i++)
9902     scores->entry[i].score = getFile16BitBE(file);
9903
9904   chunk_size = scores->num_entries * 2;
9905
9906   return chunk_size;
9907 }
9908
9909 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9910 {
9911   int i;
9912
9913   for (i = 0; i < scores->num_entries; i++)
9914     scores->entry[i].score = getFile32BitBE(file);
9915
9916   chunk_size = scores->num_entries * 4;
9917
9918   return chunk_size;
9919 }
9920
9921 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9922 {
9923   int i;
9924
9925   for (i = 0; i < scores->num_entries; i++)
9926     scores->entry[i].time = getFile32BitBE(file);
9927
9928   chunk_size = scores->num_entries * 4;
9929
9930   return chunk_size;
9931 }
9932
9933 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9934 {
9935   int i, j;
9936
9937   for (i = 0; i < scores->num_entries; i++)
9938   {
9939     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9940       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9941
9942     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9943   }
9944
9945   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9946
9947   return chunk_size;
9948 }
9949
9950 void LoadScore(int nr)
9951 {
9952   char *filename = getScoreFilename(nr);
9953   char cookie[MAX_LINE_LEN];
9954   char chunk_name[CHUNK_ID_LEN + 1];
9955   int chunk_size;
9956   boolean old_score_file_format = FALSE;
9957   File *file;
9958
9959   // always start with reliable default values
9960   setScoreInfoToDefaults();
9961
9962   if (!(file = openFile(filename, MODE_READ)))
9963     return;
9964
9965   getFileChunkBE(file, chunk_name, NULL);
9966   if (strEqual(chunk_name, "RND1"))
9967   {
9968     getFile32BitBE(file);               // not used
9969
9970     getFileChunkBE(file, chunk_name, NULL);
9971     if (!strEqual(chunk_name, "SCOR"))
9972     {
9973       Warn("unknown format of score file '%s'", filename);
9974
9975       closeFile(file);
9976
9977       return;
9978     }
9979   }
9980   else  // check for old file format with cookie string
9981   {
9982     strcpy(cookie, chunk_name);
9983     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9984       cookie[4] = '\0';
9985     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9986       cookie[strlen(cookie) - 1] = '\0';
9987
9988     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9989     {
9990       Warn("unknown format of score file '%s'", filename);
9991
9992       closeFile(file);
9993
9994       return;
9995     }
9996
9997     old_score_file_format = TRUE;
9998   }
9999
10000   if (old_score_file_format)
10001   {
10002     // score files from versions before 4.2.4.0 without chunk structure
10003     LoadScore_OLD(nr);
10004
10005     // convert score to time, if possible (mainly for Supaplex levels)
10006     ConvertScore_OLD();
10007   }
10008   else
10009   {
10010     static struct
10011     {
10012       char *name;
10013       int size;
10014       int (*loader)(File *, int, struct ScoreInfo *);
10015     }
10016     chunk_info[] =
10017     {
10018       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10019       { "INFO", -1,                     LoadScore_INFO },
10020       { "NAME", -1,                     LoadScore_NAME },
10021       { "SCOR", -1,                     LoadScore_SCOR },
10022       { "SC4R", -1,                     LoadScore_SC4R },
10023       { "TIME", -1,                     LoadScore_TIME },
10024       { "TAPE", -1,                     LoadScore_TAPE },
10025
10026       {  NULL,  0,                      NULL }
10027     };
10028
10029     while (getFileChunkBE(file, chunk_name, &chunk_size))
10030     {
10031       int i = 0;
10032
10033       while (chunk_info[i].name != NULL &&
10034              !strEqual(chunk_name, chunk_info[i].name))
10035         i++;
10036
10037       if (chunk_info[i].name == NULL)
10038       {
10039         Warn("unknown chunk '%s' in score file '%s'",
10040               chunk_name, filename);
10041
10042         ReadUnusedBytesFromFile(file, chunk_size);
10043       }
10044       else if (chunk_info[i].size != -1 &&
10045                chunk_info[i].size != chunk_size)
10046       {
10047         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10048               chunk_size, chunk_name, filename);
10049
10050         ReadUnusedBytesFromFile(file, chunk_size);
10051       }
10052       else
10053       {
10054         // call function to load this score chunk
10055         int chunk_size_expected =
10056           (chunk_info[i].loader)(file, chunk_size, &scores);
10057
10058         // the size of some chunks cannot be checked before reading other
10059         // chunks first (like "HEAD" and "BODY") that contain some header
10060         // information, so check them here
10061         if (chunk_size_expected != chunk_size)
10062         {
10063           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10064                 chunk_size, chunk_name, filename);
10065         }
10066       }
10067     }
10068   }
10069
10070   closeFile(file);
10071 }
10072
10073 #if ENABLE_HISTORIC_CHUNKS
10074 void SaveScore_OLD(int nr)
10075 {
10076   int i;
10077   char *filename = getScoreFilename(nr);
10078   FILE *file;
10079
10080   // used instead of "leveldir_current->subdir" (for network games)
10081   InitScoreDirectory(levelset.identifier);
10082
10083   if (!(file = fopen(filename, MODE_WRITE)))
10084   {
10085     Warn("cannot save score for level %d", nr);
10086
10087     return;
10088   }
10089
10090   fprintf(file, "%s\n\n", SCORE_COOKIE);
10091
10092   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10093     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10094
10095   fclose(file);
10096
10097   SetFilePermissions(filename, PERMS_PRIVATE);
10098 }
10099 #endif
10100
10101 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10102 {
10103   putFileVersion(file, scores->file_version);
10104   putFileVersion(file, scores->game_version);
10105 }
10106
10107 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10108 {
10109   int level_identifier_size = strlen(scores->level_identifier) + 1;
10110   int i;
10111
10112   putFile16BitBE(file, level_identifier_size);
10113
10114   for (i = 0; i < level_identifier_size; i++)
10115     putFile8Bit(file, scores->level_identifier[i]);
10116
10117   putFile16BitBE(file, scores->level_nr);
10118   putFile16BitBE(file, scores->num_entries);
10119 }
10120
10121 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10122 {
10123   int i, j;
10124
10125   for (i = 0; i < scores->num_entries; i++)
10126   {
10127     int name_size = strlen(scores->entry[i].name);
10128
10129     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10130       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10131   }
10132 }
10133
10134 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10135 {
10136   int i;
10137
10138   for (i = 0; i < scores->num_entries; i++)
10139     putFile16BitBE(file, scores->entry[i].score);
10140 }
10141
10142 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10143 {
10144   int i;
10145
10146   for (i = 0; i < scores->num_entries; i++)
10147     putFile32BitBE(file, scores->entry[i].score);
10148 }
10149
10150 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10151 {
10152   int i;
10153
10154   for (i = 0; i < scores->num_entries; i++)
10155     putFile32BitBE(file, scores->entry[i].time);
10156 }
10157
10158 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10159 {
10160   int i, j;
10161
10162   for (i = 0; i < scores->num_entries; i++)
10163   {
10164     int size = strlen(scores->entry[i].tape_basename);
10165
10166     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10167       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10168   }
10169 }
10170
10171 static void SaveScoreToFilename(char *filename)
10172 {
10173   FILE *file;
10174   int info_chunk_size;
10175   int name_chunk_size;
10176   int scor_chunk_size;
10177   int sc4r_chunk_size;
10178   int time_chunk_size;
10179   int tape_chunk_size;
10180   boolean has_large_score_values;
10181   int i;
10182
10183   if (!(file = fopen(filename, MODE_WRITE)))
10184   {
10185     Warn("cannot save score file '%s'", filename);
10186
10187     return;
10188   }
10189
10190   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10191   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10192   scor_chunk_size = scores.num_entries * 2;
10193   sc4r_chunk_size = scores.num_entries * 4;
10194   time_chunk_size = scores.num_entries * 4;
10195   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10196
10197   has_large_score_values = FALSE;
10198   for (i = 0; i < scores.num_entries; i++)
10199     if (scores.entry[i].score > 0xffff)
10200       has_large_score_values = TRUE;
10201
10202   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10203   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10204
10205   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10206   SaveScore_VERS(file, &scores);
10207
10208   putFileChunkBE(file, "INFO", info_chunk_size);
10209   SaveScore_INFO(file, &scores);
10210
10211   putFileChunkBE(file, "NAME", name_chunk_size);
10212   SaveScore_NAME(file, &scores);
10213
10214   if (has_large_score_values)
10215   {
10216     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10217     SaveScore_SC4R(file, &scores);
10218   }
10219   else
10220   {
10221     putFileChunkBE(file, "SCOR", scor_chunk_size);
10222     SaveScore_SCOR(file, &scores);
10223   }
10224
10225   putFileChunkBE(file, "TIME", time_chunk_size);
10226   SaveScore_TIME(file, &scores);
10227
10228   putFileChunkBE(file, "TAPE", tape_chunk_size);
10229   SaveScore_TAPE(file, &scores);
10230
10231   fclose(file);
10232
10233   SetFilePermissions(filename, PERMS_PRIVATE);
10234 }
10235
10236 void SaveScore(int nr)
10237 {
10238   char *filename = getScoreFilename(nr);
10239   int i;
10240
10241   // used instead of "leveldir_current->subdir" (for network games)
10242   InitScoreDirectory(levelset.identifier);
10243
10244   scores.file_version = FILE_VERSION_ACTUAL;
10245   scores.game_version = GAME_VERSION_ACTUAL;
10246
10247   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10248   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10249   scores.level_nr = level_nr;
10250
10251   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10252     if (scores.entry[i].score == 0 &&
10253         scores.entry[i].time == 0 &&
10254         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10255       break;
10256
10257   scores.num_entries = i;
10258
10259   if (scores.num_entries == 0)
10260     return;
10261
10262   SaveScoreToFilename(filename);
10263 }
10264
10265 static void LoadServerScoreFromCache(int nr)
10266 {
10267   struct ScoreEntry score_entry;
10268   struct
10269   {
10270     void *value;
10271     boolean is_string;
10272     int string_size;
10273   }
10274   score_mapping[] =
10275   {
10276     { &score_entry.score,               FALSE,  0                       },
10277     { &score_entry.time,                FALSE,  0                       },
10278     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10279     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10280     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10281     { &score_entry.id,                  FALSE,  0                       },
10282     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10283     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10284     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10285     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10286
10287     { NULL,                             FALSE,  0                       }
10288   };
10289   char *filename = getScoreCacheFilename(nr);
10290   SetupFileHash *score_hash = loadSetupFileHash(filename);
10291   int i, j;
10292
10293   server_scores.num_entries = 0;
10294
10295   if (score_hash == NULL)
10296     return;
10297
10298   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10299   {
10300     score_entry = server_scores.entry[i];
10301
10302     for (j = 0; score_mapping[j].value != NULL; j++)
10303     {
10304       char token[10];
10305
10306       sprintf(token, "%02d.%d", i, j);
10307
10308       char *value = getHashEntry(score_hash, token);
10309
10310       if (value == NULL)
10311         continue;
10312
10313       if (score_mapping[j].is_string)
10314       {
10315         char *score_value = (char *)score_mapping[j].value;
10316         int value_size = score_mapping[j].string_size;
10317
10318         strncpy(score_value, value, value_size);
10319         score_value[value_size] = '\0';
10320       }
10321       else
10322       {
10323         int *score_value = (int *)score_mapping[j].value;
10324
10325         *score_value = atoi(value);
10326       }
10327
10328       server_scores.num_entries = i + 1;
10329     }
10330
10331     server_scores.entry[i] = score_entry;
10332   }
10333
10334   freeSetupFileHash(score_hash);
10335 }
10336
10337 void LoadServerScore(int nr, boolean download_score)
10338 {
10339   if (!setup.use_api_server)
10340     return;
10341
10342   // always start with reliable default values
10343   setServerScoreInfoToDefaults();
10344
10345   // 1st step: load server scores from cache file (which may not exist)
10346   // (this should prevent reading it while the thread is writing to it)
10347   LoadServerScoreFromCache(nr);
10348
10349   if (download_score && runtime.use_api_server)
10350   {
10351     // 2nd step: download server scores from score server to cache file
10352     // (as thread, as it might time out if the server is not reachable)
10353     ApiGetScoreAsThread(nr);
10354   }
10355 }
10356
10357 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10358 {
10359   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10360
10361   // if score tape not uploaded, ask for uploading missing tapes later
10362   if (!setup.has_remaining_tapes)
10363     setup.ask_for_remaining_tapes = TRUE;
10364
10365   setup.provide_uploading_tapes = TRUE;
10366   setup.has_remaining_tapes = TRUE;
10367
10368   SaveSetup_ServerSetup();
10369 }
10370
10371 void SaveServerScore(int nr, boolean tape_saved)
10372 {
10373   if (!runtime.use_api_server)
10374   {
10375     PrepareScoreTapesForUpload(leveldir_current->subdir);
10376
10377     return;
10378   }
10379
10380   ApiAddScoreAsThread(nr, tape_saved, NULL);
10381 }
10382
10383 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10384                              char *score_tape_filename)
10385 {
10386   if (!runtime.use_api_server)
10387     return;
10388
10389   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10390 }
10391
10392 void LoadLocalAndServerScore(int nr, boolean download_score)
10393 {
10394   int last_added_local = scores.last_added_local;
10395   boolean force_last_added = scores.force_last_added;
10396
10397   // needed if only showing server scores
10398   setScoreInfoToDefaults();
10399
10400   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10401     LoadScore(nr);
10402
10403   // restore last added local score entry (before merging server scores)
10404   scores.last_added = scores.last_added_local = last_added_local;
10405
10406   if (setup.use_api_server &&
10407       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10408   {
10409     // load server scores from cache file and trigger update from server
10410     LoadServerScore(nr, download_score);
10411
10412     // merge local scores with scores from server
10413     MergeServerScore();
10414   }
10415
10416   if (force_last_added)
10417     scores.force_last_added = force_last_added;
10418 }
10419
10420
10421 // ============================================================================
10422 // setup file functions
10423 // ============================================================================
10424
10425 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10426
10427
10428 static struct TokenInfo global_setup_tokens[] =
10429 {
10430   {
10431     TYPE_STRING,
10432     &setup.player_name,                         "player_name"
10433   },
10434   {
10435     TYPE_SWITCH,
10436     &setup.multiple_users,                      "multiple_users"
10437   },
10438   {
10439     TYPE_SWITCH,
10440     &setup.sound,                               "sound"
10441   },
10442   {
10443     TYPE_SWITCH,
10444     &setup.sound_loops,                         "repeating_sound_loops"
10445   },
10446   {
10447     TYPE_SWITCH,
10448     &setup.sound_music,                         "background_music"
10449   },
10450   {
10451     TYPE_SWITCH,
10452     &setup.sound_simple,                        "simple_sound_effects"
10453   },
10454   {
10455     TYPE_SWITCH,
10456     &setup.toons,                               "toons"
10457   },
10458   {
10459     TYPE_SWITCH,
10460     &setup.global_animations,                   "global_animations"
10461   },
10462   {
10463     TYPE_SWITCH,
10464     &setup.scroll_delay,                        "scroll_delay"
10465   },
10466   {
10467     TYPE_SWITCH,
10468     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10469   },
10470   {
10471     TYPE_INTEGER,
10472     &setup.scroll_delay_value,                  "scroll_delay_value"
10473   },
10474   {
10475     TYPE_STRING,
10476     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10477   },
10478   {
10479     TYPE_INTEGER,
10480     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10481   },
10482   {
10483     TYPE_SWITCH,
10484     &setup.fade_screens,                        "fade_screens"
10485   },
10486   {
10487     TYPE_SWITCH,
10488     &setup.autorecord,                          "automatic_tape_recording"
10489   },
10490   {
10491     TYPE_SWITCH,
10492     &setup.autorecord_after_replay,             "autorecord_after_replay"
10493   },
10494   {
10495     TYPE_SWITCH,
10496     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10497   },
10498   {
10499     TYPE_SWITCH,
10500     &setup.show_titlescreen,                    "show_titlescreen"
10501   },
10502   {
10503     TYPE_SWITCH,
10504     &setup.quick_doors,                         "quick_doors"
10505   },
10506   {
10507     TYPE_SWITCH,
10508     &setup.team_mode,                           "team_mode"
10509   },
10510   {
10511     TYPE_SWITCH,
10512     &setup.handicap,                            "handicap"
10513   },
10514   {
10515     TYPE_SWITCH,
10516     &setup.skip_levels,                         "skip_levels"
10517   },
10518   {
10519     TYPE_SWITCH,
10520     &setup.increment_levels,                    "increment_levels"
10521   },
10522   {
10523     TYPE_SWITCH,
10524     &setup.auto_play_next_level,                "auto_play_next_level"
10525   },
10526   {
10527     TYPE_SWITCH,
10528     &setup.count_score_after_game,              "count_score_after_game"
10529   },
10530   {
10531     TYPE_SWITCH,
10532     &setup.show_scores_after_game,              "show_scores_after_game"
10533   },
10534   {
10535     TYPE_SWITCH,
10536     &setup.time_limit,                          "time_limit"
10537   },
10538   {
10539     TYPE_SWITCH,
10540     &setup.fullscreen,                          "fullscreen"
10541   },
10542   {
10543     TYPE_INTEGER,
10544     &setup.window_scaling_percent,              "window_scaling_percent"
10545   },
10546   {
10547     TYPE_STRING,
10548     &setup.window_scaling_quality,              "window_scaling_quality"
10549   },
10550   {
10551     TYPE_STRING,
10552     &setup.screen_rendering_mode,               "screen_rendering_mode"
10553   },
10554   {
10555     TYPE_STRING,
10556     &setup.vsync_mode,                          "vsync_mode"
10557   },
10558   {
10559     TYPE_SWITCH,
10560     &setup.ask_on_escape,                       "ask_on_escape"
10561   },
10562   {
10563     TYPE_SWITCH,
10564     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10565   },
10566   {
10567     TYPE_SWITCH,
10568     &setup.ask_on_game_over,                    "ask_on_game_over"
10569   },
10570   {
10571     TYPE_SWITCH,
10572     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10573   },
10574   {
10575     TYPE_SWITCH,
10576     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10577   },
10578   {
10579     TYPE_SWITCH,
10580     &setup.quick_switch,                        "quick_player_switch"
10581   },
10582   {
10583     TYPE_SWITCH,
10584     &setup.input_on_focus,                      "input_on_focus"
10585   },
10586   {
10587     TYPE_SWITCH,
10588     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10589   },
10590   {
10591     TYPE_SWITCH,
10592     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10593   },
10594   {
10595     TYPE_SWITCH,
10596     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10597   },
10598   {
10599     TYPE_SWITCH,
10600     &setup.game_speed_extended,                 "game_speed_extended"
10601   },
10602   {
10603     TYPE_INTEGER,
10604     &setup.game_frame_delay,                    "game_frame_delay"
10605   },
10606   {
10607     TYPE_SWITCH,
10608     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10609   },
10610   {
10611     TYPE_SWITCH,
10612     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10617   },
10618   {
10619     TYPE_SWITCH3,
10620     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10621   },
10622   {
10623     TYPE_SWITCH,
10624     &setup.sp_show_border_elements,             "sp_show_border_elements"
10625   },
10626   {
10627     TYPE_SWITCH,
10628     &setup.small_game_graphics,                 "small_game_graphics"
10629   },
10630   {
10631     TYPE_SWITCH,
10632     &setup.show_load_save_buttons,              "show_load_save_buttons"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10637   },
10638   {
10639     TYPE_STRING,
10640     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10641   },
10642   {
10643     TYPE_STRING,
10644     &setup.graphics_set,                        "graphics_set"
10645   },
10646   {
10647     TYPE_STRING,
10648     &setup.sounds_set,                          "sounds_set"
10649   },
10650   {
10651     TYPE_STRING,
10652     &setup.music_set,                           "music_set"
10653   },
10654   {
10655     TYPE_SWITCH3,
10656     &setup.override_level_graphics,             "override_level_graphics"
10657   },
10658   {
10659     TYPE_SWITCH3,
10660     &setup.override_level_sounds,               "override_level_sounds"
10661   },
10662   {
10663     TYPE_SWITCH3,
10664     &setup.override_level_music,                "override_level_music"
10665   },
10666   {
10667     TYPE_INTEGER,
10668     &setup.volume_simple,                       "volume_simple"
10669   },
10670   {
10671     TYPE_INTEGER,
10672     &setup.volume_loops,                        "volume_loops"
10673   },
10674   {
10675     TYPE_INTEGER,
10676     &setup.volume_music,                        "volume_music"
10677   },
10678   {
10679     TYPE_SWITCH,
10680     &setup.network_mode,                        "network_mode"
10681   },
10682   {
10683     TYPE_PLAYER,
10684     &setup.network_player_nr,                   "network_player"
10685   },
10686   {
10687     TYPE_STRING,
10688     &setup.network_server_hostname,             "network_server_hostname"
10689   },
10690   {
10691     TYPE_STRING,
10692     &setup.touch.control_type,                  "touch.control_type"
10693   },
10694   {
10695     TYPE_INTEGER,
10696     &setup.touch.move_distance,                 "touch.move_distance"
10697   },
10698   {
10699     TYPE_INTEGER,
10700     &setup.touch.drop_distance,                 "touch.drop_distance"
10701   },
10702   {
10703     TYPE_INTEGER,
10704     &setup.touch.transparency,                  "touch.transparency"
10705   },
10706   {
10707     TYPE_INTEGER,
10708     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10709   },
10710   {
10711     TYPE_INTEGER,
10712     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10713   },
10714   {
10715     TYPE_INTEGER,
10716     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10717   },
10718   {
10719     TYPE_INTEGER,
10720     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10721   },
10722   {
10723     TYPE_INTEGER,
10724     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10725   },
10726   {
10727     TYPE_INTEGER,
10728     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10729   },
10730   {
10731     TYPE_SWITCH,
10732     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10733   },
10734 };
10735
10736 static struct TokenInfo auto_setup_tokens[] =
10737 {
10738   {
10739     TYPE_INTEGER,
10740     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10741   },
10742 };
10743
10744 static struct TokenInfo server_setup_tokens[] =
10745 {
10746   {
10747     TYPE_STRING,
10748     &setup.player_uuid,                         "player_uuid"
10749   },
10750   {
10751     TYPE_INTEGER,
10752     &setup.player_version,                      "player_version"
10753   },
10754   {
10755     TYPE_SWITCH,
10756     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10757   },
10758   {
10759     TYPE_STRING,
10760     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10761   },
10762   {
10763     TYPE_STRING,
10764     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10765   },
10766   {
10767     TYPE_SWITCH,
10768     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10769   },
10770   {
10771     TYPE_SWITCH,
10772     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10773   },
10774   {
10775     TYPE_SWITCH,
10776     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10777   },
10778   {
10779     TYPE_SWITCH,
10780     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10781   },
10782   {
10783     TYPE_SWITCH,
10784     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10785   },
10786 };
10787
10788 static struct TokenInfo editor_setup_tokens[] =
10789 {
10790   {
10791     TYPE_SWITCH,
10792     &setup.editor.el_classic,                   "editor.el_classic"
10793   },
10794   {
10795     TYPE_SWITCH,
10796     &setup.editor.el_custom,                    "editor.el_custom"
10797   },
10798   {
10799     TYPE_SWITCH,
10800     &setup.editor.el_user_defined,              "editor.el_user_defined"
10801   },
10802   {
10803     TYPE_SWITCH,
10804     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10805   },
10806   {
10807     TYPE_SWITCH,
10808     &setup.editor.el_headlines,                 "editor.el_headlines"
10809   },
10810   {
10811     TYPE_SWITCH,
10812     &setup.editor.show_element_token,           "editor.show_element_token"
10813   },
10814   {
10815     TYPE_SWITCH,
10816     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10817   },
10818 };
10819
10820 static struct TokenInfo editor_cascade_setup_tokens[] =
10821 {
10822   {
10823     TYPE_SWITCH,
10824     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10825   },
10826   {
10827     TYPE_SWITCH,
10828     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10829   },
10830   {
10831     TYPE_SWITCH,
10832     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10833   },
10834   {
10835     TYPE_SWITCH,
10836     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10837   },
10838   {
10839     TYPE_SWITCH,
10840     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10841   },
10842   {
10843     TYPE_SWITCH,
10844     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10845   },
10846   {
10847     TYPE_SWITCH,
10848     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10849   },
10850   {
10851     TYPE_SWITCH,
10852     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10853   },
10854   {
10855     TYPE_SWITCH,
10856     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10857   },
10858   {
10859     TYPE_SWITCH,
10860     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10861   },
10862   {
10863     TYPE_SWITCH,
10864     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10865   },
10866   {
10867     TYPE_SWITCH,
10868     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10869   },
10870   {
10871     TYPE_SWITCH,
10872     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10873   },
10874   {
10875     TYPE_SWITCH,
10876     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10877   },
10878   {
10879     TYPE_SWITCH,
10880     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10881   },
10882   {
10883     TYPE_SWITCH,
10884     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10885   },
10886   {
10887     TYPE_SWITCH,
10888     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10889   },
10890   {
10891     TYPE_SWITCH,
10892     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10893   },
10894   {
10895     TYPE_SWITCH,
10896     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10897   },
10898   {
10899     TYPE_SWITCH,
10900     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10901   },
10902 };
10903
10904 static struct TokenInfo shortcut_setup_tokens[] =
10905 {
10906   {
10907     TYPE_KEY_X11,
10908     &setup.shortcut.save_game,                  "shortcut.save_game"
10909   },
10910   {
10911     TYPE_KEY_X11,
10912     &setup.shortcut.load_game,                  "shortcut.load_game"
10913   },
10914   {
10915     TYPE_KEY_X11,
10916     &setup.shortcut.restart_game,               "shortcut.restart_game"
10917   },
10918   {
10919     TYPE_KEY_X11,
10920     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10921   },
10922   {
10923     TYPE_KEY_X11,
10924     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10925   },
10926   {
10927     TYPE_KEY_X11,
10928     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10929   },
10930   {
10931     TYPE_KEY_X11,
10932     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10933   },
10934   {
10935     TYPE_KEY_X11,
10936     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10937   },
10938   {
10939     TYPE_KEY_X11,
10940     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10941   },
10942   {
10943     TYPE_KEY_X11,
10944     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10945   },
10946   {
10947     TYPE_KEY_X11,
10948     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10949   },
10950   {
10951     TYPE_KEY_X11,
10952     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10953   },
10954   {
10955     TYPE_KEY_X11,
10956     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10957   },
10958   {
10959     TYPE_KEY_X11,
10960     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10961   },
10962   {
10963     TYPE_KEY_X11,
10964     &setup.shortcut.tape_record,                "shortcut.tape_record"
10965   },
10966   {
10967     TYPE_KEY_X11,
10968     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10969   },
10970   {
10971     TYPE_KEY_X11,
10972     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10973   },
10974   {
10975     TYPE_KEY_X11,
10976     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10977   },
10978   {
10979     TYPE_KEY_X11,
10980     &setup.shortcut.sound_music,                "shortcut.sound_music"
10981   },
10982   {
10983     TYPE_KEY_X11,
10984     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10985   },
10986   {
10987     TYPE_KEY_X11,
10988     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10989   },
10990   {
10991     TYPE_KEY_X11,
10992     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10993   },
10994   {
10995     TYPE_KEY_X11,
10996     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10997   },
10998 };
10999
11000 static struct SetupInputInfo setup_input;
11001 static struct TokenInfo player_setup_tokens[] =
11002 {
11003   {
11004     TYPE_BOOLEAN,
11005     &setup_input.use_joystick,                  ".use_joystick"
11006   },
11007   {
11008     TYPE_STRING,
11009     &setup_input.joy.device_name,               ".joy.device_name"
11010   },
11011   {
11012     TYPE_INTEGER,
11013     &setup_input.joy.xleft,                     ".joy.xleft"
11014   },
11015   {
11016     TYPE_INTEGER,
11017     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11018   },
11019   {
11020     TYPE_INTEGER,
11021     &setup_input.joy.xright,                    ".joy.xright"
11022   },
11023   {
11024     TYPE_INTEGER,
11025     &setup_input.joy.yupper,                    ".joy.yupper"
11026   },
11027   {
11028     TYPE_INTEGER,
11029     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11030   },
11031   {
11032     TYPE_INTEGER,
11033     &setup_input.joy.ylower,                    ".joy.ylower"
11034   },
11035   {
11036     TYPE_INTEGER,
11037     &setup_input.joy.snap,                      ".joy.snap_field"
11038   },
11039   {
11040     TYPE_INTEGER,
11041     &setup_input.joy.drop,                      ".joy.place_bomb"
11042   },
11043   {
11044     TYPE_KEY_X11,
11045     &setup_input.key.left,                      ".key.move_left"
11046   },
11047   {
11048     TYPE_KEY_X11,
11049     &setup_input.key.right,                     ".key.move_right"
11050   },
11051   {
11052     TYPE_KEY_X11,
11053     &setup_input.key.up,                        ".key.move_up"
11054   },
11055   {
11056     TYPE_KEY_X11,
11057     &setup_input.key.down,                      ".key.move_down"
11058   },
11059   {
11060     TYPE_KEY_X11,
11061     &setup_input.key.snap,                      ".key.snap_field"
11062   },
11063   {
11064     TYPE_KEY_X11,
11065     &setup_input.key.drop,                      ".key.place_bomb"
11066   },
11067 };
11068
11069 static struct TokenInfo system_setup_tokens[] =
11070 {
11071   {
11072     TYPE_STRING,
11073     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11074   },
11075   {
11076     TYPE_STRING,
11077     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11078   },
11079   {
11080     TYPE_STRING,
11081     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11082   },
11083   {
11084     TYPE_INTEGER,
11085     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11086   },
11087 };
11088
11089 static struct TokenInfo internal_setup_tokens[] =
11090 {
11091   {
11092     TYPE_STRING,
11093     &setup.internal.program_title,              "program_title"
11094   },
11095   {
11096     TYPE_STRING,
11097     &setup.internal.program_version,            "program_version"
11098   },
11099   {
11100     TYPE_STRING,
11101     &setup.internal.program_author,             "program_author"
11102   },
11103   {
11104     TYPE_STRING,
11105     &setup.internal.program_email,              "program_email"
11106   },
11107   {
11108     TYPE_STRING,
11109     &setup.internal.program_website,            "program_website"
11110   },
11111   {
11112     TYPE_STRING,
11113     &setup.internal.program_copyright,          "program_copyright"
11114   },
11115   {
11116     TYPE_STRING,
11117     &setup.internal.program_company,            "program_company"
11118   },
11119   {
11120     TYPE_STRING,
11121     &setup.internal.program_icon_file,          "program_icon_file"
11122   },
11123   {
11124     TYPE_STRING,
11125     &setup.internal.default_graphics_set,       "default_graphics_set"
11126   },
11127   {
11128     TYPE_STRING,
11129     &setup.internal.default_sounds_set,         "default_sounds_set"
11130   },
11131   {
11132     TYPE_STRING,
11133     &setup.internal.default_music_set,          "default_music_set"
11134   },
11135   {
11136     TYPE_STRING,
11137     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11138   },
11139   {
11140     TYPE_STRING,
11141     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11142   },
11143   {
11144     TYPE_STRING,
11145     &setup.internal.fallback_music_file,        "fallback_music_file"
11146   },
11147   {
11148     TYPE_STRING,
11149     &setup.internal.default_level_series,       "default_level_series"
11150   },
11151   {
11152     TYPE_INTEGER,
11153     &setup.internal.default_window_width,       "default_window_width"
11154   },
11155   {
11156     TYPE_INTEGER,
11157     &setup.internal.default_window_height,      "default_window_height"
11158   },
11159   {
11160     TYPE_BOOLEAN,
11161     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11162   },
11163   {
11164     TYPE_BOOLEAN,
11165     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11166   },
11167   {
11168     TYPE_BOOLEAN,
11169     &setup.internal.create_user_levelset,       "create_user_levelset"
11170   },
11171   {
11172     TYPE_BOOLEAN,
11173     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11174   },
11175   {
11176     TYPE_BOOLEAN,
11177     &setup.internal.menu_game,                  "menu_game"
11178   },
11179   {
11180     TYPE_BOOLEAN,
11181     &setup.internal.menu_engines,               "menu_engines"
11182   },
11183   {
11184     TYPE_BOOLEAN,
11185     &setup.internal.menu_editor,                "menu_editor"
11186   },
11187   {
11188     TYPE_BOOLEAN,
11189     &setup.internal.menu_graphics,              "menu_graphics"
11190   },
11191   {
11192     TYPE_BOOLEAN,
11193     &setup.internal.menu_sound,                 "menu_sound"
11194   },
11195   {
11196     TYPE_BOOLEAN,
11197     &setup.internal.menu_artwork,               "menu_artwork"
11198   },
11199   {
11200     TYPE_BOOLEAN,
11201     &setup.internal.menu_input,                 "menu_input"
11202   },
11203   {
11204     TYPE_BOOLEAN,
11205     &setup.internal.menu_touch,                 "menu_touch"
11206   },
11207   {
11208     TYPE_BOOLEAN,
11209     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11210   },
11211   {
11212     TYPE_BOOLEAN,
11213     &setup.internal.menu_exit,                  "menu_exit"
11214   },
11215   {
11216     TYPE_BOOLEAN,
11217     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11218   },
11219   {
11220     TYPE_BOOLEAN,
11221     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11222   },
11223   {
11224     TYPE_BOOLEAN,
11225     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11226   },
11227   {
11228     TYPE_BOOLEAN,
11229     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11230   },
11231   {
11232     TYPE_BOOLEAN,
11233     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11234   },
11235   {
11236     TYPE_BOOLEAN,
11237     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11238   },
11239   {
11240     TYPE_BOOLEAN,
11241     &setup.internal.info_title,                 "info_title"
11242   },
11243   {
11244     TYPE_BOOLEAN,
11245     &setup.internal.info_elements,              "info_elements"
11246   },
11247   {
11248     TYPE_BOOLEAN,
11249     &setup.internal.info_music,                 "info_music"
11250   },
11251   {
11252     TYPE_BOOLEAN,
11253     &setup.internal.info_credits,               "info_credits"
11254   },
11255   {
11256     TYPE_BOOLEAN,
11257     &setup.internal.info_program,               "info_program"
11258   },
11259   {
11260     TYPE_BOOLEAN,
11261     &setup.internal.info_version,               "info_version"
11262   },
11263   {
11264     TYPE_BOOLEAN,
11265     &setup.internal.info_levelset,              "info_levelset"
11266   },
11267   {
11268     TYPE_BOOLEAN,
11269     &setup.internal.info_exit,                  "info_exit"
11270   },
11271 };
11272
11273 static struct TokenInfo debug_setup_tokens[] =
11274 {
11275   {
11276     TYPE_INTEGER,
11277     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11278   },
11279   {
11280     TYPE_INTEGER,
11281     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11282   },
11283   {
11284     TYPE_INTEGER,
11285     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11286   },
11287   {
11288     TYPE_INTEGER,
11289     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11290   },
11291   {
11292     TYPE_INTEGER,
11293     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11294   },
11295   {
11296     TYPE_INTEGER,
11297     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11298   },
11299   {
11300     TYPE_INTEGER,
11301     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11302   },
11303   {
11304     TYPE_INTEGER,
11305     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11306   },
11307   {
11308     TYPE_INTEGER,
11309     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11310   },
11311   {
11312     TYPE_INTEGER,
11313     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11314   },
11315   {
11316     TYPE_KEY_X11,
11317     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11318   },
11319   {
11320     TYPE_KEY_X11,
11321     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11322   },
11323   {
11324     TYPE_KEY_X11,
11325     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11326   },
11327   {
11328     TYPE_KEY_X11,
11329     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11330   },
11331   {
11332     TYPE_KEY_X11,
11333     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11334   },
11335   {
11336     TYPE_KEY_X11,
11337     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11338   },
11339   {
11340     TYPE_KEY_X11,
11341     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11342   },
11343   {
11344     TYPE_KEY_X11,
11345     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11346   },
11347   {
11348     TYPE_KEY_X11,
11349     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11350   },
11351   {
11352     TYPE_KEY_X11,
11353     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11354   },
11355   {
11356     TYPE_BOOLEAN,
11357     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11358   {
11359     TYPE_BOOLEAN,
11360     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11361   },
11362   {
11363     TYPE_BOOLEAN,
11364     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11365   },
11366   {
11367     TYPE_SWITCH3,
11368     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11369   },
11370   {
11371     TYPE_INTEGER,
11372     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11373   },
11374 };
11375
11376 static struct TokenInfo options_setup_tokens[] =
11377 {
11378   {
11379     TYPE_BOOLEAN,
11380     &setup.options.verbose,                     "options.verbose"
11381   },
11382   {
11383     TYPE_BOOLEAN,
11384     &setup.options.debug,                       "options.debug"
11385   },
11386   {
11387     TYPE_STRING,
11388     &setup.options.debug_mode,                  "options.debug_mode"
11389   },
11390 };
11391
11392 static void setSetupInfoToDefaults(struct SetupInfo *si)
11393 {
11394   int i;
11395
11396   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11397
11398   si->multiple_users = TRUE;
11399
11400   si->sound = TRUE;
11401   si->sound_loops = TRUE;
11402   si->sound_music = TRUE;
11403   si->sound_simple = TRUE;
11404   si->toons = TRUE;
11405   si->global_animations = TRUE;
11406   si->scroll_delay = TRUE;
11407   si->forced_scroll_delay = FALSE;
11408   si->scroll_delay_value = STD_SCROLL_DELAY;
11409   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11410   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11411   si->fade_screens = TRUE;
11412   si->autorecord = TRUE;
11413   si->autorecord_after_replay = TRUE;
11414   si->auto_pause_on_start = FALSE;
11415   si->show_titlescreen = TRUE;
11416   si->quick_doors = FALSE;
11417   si->team_mode = FALSE;
11418   si->handicap = TRUE;
11419   si->skip_levels = TRUE;
11420   si->increment_levels = TRUE;
11421   si->auto_play_next_level = TRUE;
11422   si->count_score_after_game = TRUE;
11423   si->show_scores_after_game = TRUE;
11424   si->time_limit = TRUE;
11425   si->fullscreen = FALSE;
11426   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11427   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11428   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11429   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11430   si->ask_on_escape = TRUE;
11431   si->ask_on_escape_editor = TRUE;
11432   si->ask_on_game_over = TRUE;
11433   si->ask_on_quit_game = TRUE;
11434   si->ask_on_quit_program = TRUE;
11435   si->quick_switch = FALSE;
11436   si->input_on_focus = FALSE;
11437   si->prefer_aga_graphics = TRUE;
11438   si->prefer_lowpass_sounds = FALSE;
11439   si->prefer_extra_panel_items = TRUE;
11440   si->game_speed_extended = FALSE;
11441   si->game_frame_delay = GAME_FRAME_DELAY;
11442   si->bd_skip_uncovering = FALSE;
11443   si->bd_skip_hatching = FALSE;
11444   si->bd_scroll_delay = TRUE;
11445   si->bd_smooth_movements = AUTO;
11446   si->sp_show_border_elements = FALSE;
11447   si->small_game_graphics = FALSE;
11448   si->show_load_save_buttons = FALSE;
11449   si->show_undo_redo_buttons = FALSE;
11450   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11451
11452   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11453   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11454   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11455
11456   si->override_level_graphics = FALSE;
11457   si->override_level_sounds = FALSE;
11458   si->override_level_music = FALSE;
11459
11460   si->volume_simple = 100;              // percent
11461   si->volume_loops = 100;               // percent
11462   si->volume_music = 100;               // percent
11463
11464   si->network_mode = FALSE;
11465   si->network_player_nr = 0;            // first player
11466   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11467
11468   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11469   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11470   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11471   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11472   si->touch.draw_outlined = TRUE;
11473   si->touch.draw_pressed = TRUE;
11474
11475   for (i = 0; i < 2; i++)
11476   {
11477     char *default_grid_button[6][2] =
11478     {
11479       { "      ", "  ^^  " },
11480       { "      ", "  ^^  " },
11481       { "      ", "<<  >>" },
11482       { "      ", "<<  >>" },
11483       { "111222", "  vv  " },
11484       { "111222", "  vv  " }
11485     };
11486     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11487     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11488     int min_xsize = MIN(6, grid_xsize);
11489     int min_ysize = MIN(6, grid_ysize);
11490     int startx = grid_xsize - min_xsize;
11491     int starty = grid_ysize - min_ysize;
11492     int x, y;
11493
11494     // virtual buttons grid can only be set to defaults if video is initialized
11495     // (this will be repeated if virtual buttons are not loaded from setup file)
11496     if (video.initialized)
11497     {
11498       si->touch.grid_xsize[i] = grid_xsize;
11499       si->touch.grid_ysize[i] = grid_ysize;
11500     }
11501     else
11502     {
11503       si->touch.grid_xsize[i] = -1;
11504       si->touch.grid_ysize[i] = -1;
11505     }
11506
11507     for (x = 0; x < MAX_GRID_XSIZE; x++)
11508       for (y = 0; y < MAX_GRID_YSIZE; y++)
11509         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11510
11511     for (x = 0; x < min_xsize; x++)
11512       for (y = 0; y < min_ysize; y++)
11513         si->touch.grid_button[i][x][starty + y] =
11514           default_grid_button[y][0][x];
11515
11516     for (x = 0; x < min_xsize; x++)
11517       for (y = 0; y < min_ysize; y++)
11518         si->touch.grid_button[i][startx + x][starty + y] =
11519           default_grid_button[y][1][x];
11520   }
11521
11522   si->touch.grid_initialized            = video.initialized;
11523
11524   si->touch.overlay_buttons             = FALSE;
11525
11526   si->editor.el_boulderdash             = TRUE;
11527   si->editor.el_boulderdash_native      = TRUE;
11528   si->editor.el_boulderdash_effects     = TRUE;
11529   si->editor.el_emerald_mine            = TRUE;
11530   si->editor.el_emerald_mine_club       = TRUE;
11531   si->editor.el_more                    = TRUE;
11532   si->editor.el_sokoban                 = TRUE;
11533   si->editor.el_supaplex                = TRUE;
11534   si->editor.el_diamond_caves           = TRUE;
11535   si->editor.el_dx_boulderdash          = TRUE;
11536
11537   si->editor.el_mirror_magic            = TRUE;
11538   si->editor.el_deflektor               = TRUE;
11539
11540   si->editor.el_chars                   = TRUE;
11541   si->editor.el_steel_chars             = TRUE;
11542
11543   si->editor.el_classic                 = TRUE;
11544   si->editor.el_custom                  = TRUE;
11545
11546   si->editor.el_user_defined            = FALSE;
11547   si->editor.el_dynamic                 = TRUE;
11548
11549   si->editor.el_headlines               = TRUE;
11550
11551   si->editor.show_element_token         = FALSE;
11552
11553   si->editor.show_read_only_warning     = TRUE;
11554
11555   si->editor.use_template_for_new_levels = TRUE;
11556
11557   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11558   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11559   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11560   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11561   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11562
11563   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11564   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11565   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11566   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11567   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11568
11569   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11570   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11571   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11572   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11573   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11574   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11575
11576   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11577   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11578   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11579
11580   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11581   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11582   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11583   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11584
11585   for (i = 0; i < MAX_PLAYERS; i++)
11586   {
11587     si->input[i].use_joystick = FALSE;
11588     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11589     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11590     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11591     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11592     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11593     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11594     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11595     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11596     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11597     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11598     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11599     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11600     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11601     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11602     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11603   }
11604
11605   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11606   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11607   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11608   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11609
11610   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11611   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11612   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11613   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11614   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11615   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11616   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11617
11618   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11619
11620   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11621   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11622   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11623
11624   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11625   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11626   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11627
11628   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11629   si->internal.choose_from_top_leveldir = FALSE;
11630   si->internal.show_scaling_in_title = TRUE;
11631   si->internal.create_user_levelset = TRUE;
11632   si->internal.info_screens_from_main = FALSE;
11633
11634   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11635   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11636
11637   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11638   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11639   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11640   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11641   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11642   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11643   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11644   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11645   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11646   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11647
11648   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11649   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11650   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11651   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11652   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11653   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11654   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11655   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11656   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11657   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11658
11659   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11660   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11661
11662   si->debug.show_frames_per_second = FALSE;
11663
11664   si->debug.xsn_mode = AUTO;
11665   si->debug.xsn_percent = 0;
11666
11667   si->options.verbose = FALSE;
11668   si->options.debug = FALSE;
11669   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11670
11671 #if defined(PLATFORM_ANDROID)
11672   si->fullscreen = TRUE;
11673   si->touch.overlay_buttons = TRUE;
11674 #endif
11675
11676   setHideSetupEntry(&setup.debug.xsn_mode);
11677 }
11678
11679 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11680 {
11681   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11682 }
11683
11684 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11685 {
11686   si->player_uuid = NULL;       // (will be set later)
11687   si->player_version = 1;       // (will be set later)
11688
11689   si->use_api_server = TRUE;
11690   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11691   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11692   si->ask_for_uploading_tapes = TRUE;
11693   si->ask_for_remaining_tapes = FALSE;
11694   si->provide_uploading_tapes = TRUE;
11695   si->ask_for_using_api_server = TRUE;
11696   si->has_remaining_tapes = FALSE;
11697 }
11698
11699 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11700 {
11701   si->editor_cascade.el_bd              = TRUE;
11702   si->editor_cascade.el_bd_native       = TRUE;
11703   si->editor_cascade.el_bd_effects      = FALSE;
11704   si->editor_cascade.el_em              = TRUE;
11705   si->editor_cascade.el_emc             = TRUE;
11706   si->editor_cascade.el_rnd             = TRUE;
11707   si->editor_cascade.el_sb              = TRUE;
11708   si->editor_cascade.el_sp              = TRUE;
11709   si->editor_cascade.el_dc              = TRUE;
11710   si->editor_cascade.el_dx              = TRUE;
11711
11712   si->editor_cascade.el_mm              = TRUE;
11713   si->editor_cascade.el_df              = TRUE;
11714
11715   si->editor_cascade.el_chars           = FALSE;
11716   si->editor_cascade.el_steel_chars     = FALSE;
11717   si->editor_cascade.el_ce              = FALSE;
11718   si->editor_cascade.el_ge              = FALSE;
11719   si->editor_cascade.el_es              = FALSE;
11720   si->editor_cascade.el_ref             = FALSE;
11721   si->editor_cascade.el_user            = FALSE;
11722   si->editor_cascade.el_dynamic         = FALSE;
11723 }
11724
11725 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11726
11727 static char *getHideSetupToken(void *setup_value)
11728 {
11729   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11730
11731   if (setup_value != NULL)
11732     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11733
11734   return hide_setup_token;
11735 }
11736
11737 void setHideSetupEntry(void *setup_value)
11738 {
11739   char *hide_setup_token = getHideSetupToken(setup_value);
11740
11741   if (hide_setup_hash == NULL)
11742     hide_setup_hash = newSetupFileHash();
11743
11744   if (setup_value != NULL)
11745     setHashEntry(hide_setup_hash, hide_setup_token, "");
11746 }
11747
11748 void removeHideSetupEntry(void *setup_value)
11749 {
11750   char *hide_setup_token = getHideSetupToken(setup_value);
11751
11752   if (setup_value != NULL)
11753     removeHashEntry(hide_setup_hash, hide_setup_token);
11754 }
11755
11756 boolean hideSetupEntry(void *setup_value)
11757 {
11758   char *hide_setup_token = getHideSetupToken(setup_value);
11759
11760   return (setup_value != NULL &&
11761           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11762 }
11763
11764 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11765                                       struct TokenInfo *token_info,
11766                                       int token_nr, char *token_text)
11767 {
11768   char *token_hide_text = getStringCat2(token_text, ".hide");
11769   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11770
11771   // set the value of this setup option in the setup option structure
11772   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11773
11774   // check if this setup option should be hidden in the setup menu
11775   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11776     setHideSetupEntry(token_info[token_nr].value);
11777
11778   free(token_hide_text);
11779 }
11780
11781 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11782                                       struct TokenInfo *token_info,
11783                                       int token_nr)
11784 {
11785   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11786                             token_info[token_nr].text);
11787 }
11788
11789 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11790 {
11791   int i, pnr;
11792
11793   if (!setup_file_hash)
11794     return;
11795
11796   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11797     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11798
11799   setup.touch.grid_initialized = TRUE;
11800   for (i = 0; i < 2; i++)
11801   {
11802     int grid_xsize = setup.touch.grid_xsize[i];
11803     int grid_ysize = setup.touch.grid_ysize[i];
11804     int x, y;
11805
11806     // if virtual buttons are not loaded from setup file, repeat initializing
11807     // virtual buttons grid with default values later when video is initialized
11808     if (grid_xsize == -1 ||
11809         grid_ysize == -1)
11810     {
11811       setup.touch.grid_initialized = FALSE;
11812
11813       continue;
11814     }
11815
11816     for (y = 0; y < grid_ysize; y++)
11817     {
11818       char token_string[MAX_LINE_LEN];
11819
11820       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11821
11822       char *value_string = getHashEntry(setup_file_hash, token_string);
11823
11824       if (value_string == NULL)
11825         continue;
11826
11827       for (x = 0; x < grid_xsize; x++)
11828       {
11829         char c = value_string[x];
11830
11831         setup.touch.grid_button[i][x][y] =
11832           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11833       }
11834     }
11835   }
11836
11837   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11838     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11839
11840   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11841     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11842
11843   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11844   {
11845     char prefix[30];
11846
11847     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11848
11849     setup_input = setup.input[pnr];
11850     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11851     {
11852       char full_token[100];
11853
11854       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11855       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11856                                 full_token);
11857     }
11858     setup.input[pnr] = setup_input;
11859   }
11860
11861   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11862     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11863
11864   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11865     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11866
11867   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11868     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11869
11870   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11871     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11872
11873   setHideRelatedSetupEntries();
11874 }
11875
11876 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11877 {
11878   int i;
11879
11880   if (!setup_file_hash)
11881     return;
11882
11883   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11884     setSetupInfo(auto_setup_tokens, i,
11885                  getHashEntry(setup_file_hash,
11886                               auto_setup_tokens[i].text));
11887 }
11888
11889 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11890 {
11891   int i;
11892
11893   if (!setup_file_hash)
11894     return;
11895
11896   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11897     setSetupInfo(server_setup_tokens, i,
11898                  getHashEntry(setup_file_hash,
11899                               server_setup_tokens[i].text));
11900 }
11901
11902 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11903 {
11904   int i;
11905
11906   if (!setup_file_hash)
11907     return;
11908
11909   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11910     setSetupInfo(editor_cascade_setup_tokens, i,
11911                  getHashEntry(setup_file_hash,
11912                               editor_cascade_setup_tokens[i].text));
11913 }
11914
11915 void LoadUserNames(void)
11916 {
11917   int last_user_nr = user.nr;
11918   int i;
11919
11920   if (global.user_names != NULL)
11921   {
11922     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11923       checked_free(global.user_names[i]);
11924
11925     checked_free(global.user_names);
11926   }
11927
11928   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11929
11930   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11931   {
11932     user.nr = i;
11933
11934     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11935
11936     if (setup_file_hash)
11937     {
11938       char *player_name = getHashEntry(setup_file_hash, "player_name");
11939
11940       global.user_names[i] = getFixedUserName(player_name);
11941
11942       freeSetupFileHash(setup_file_hash);
11943     }
11944
11945     if (global.user_names[i] == NULL)
11946       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11947   }
11948
11949   user.nr = last_user_nr;
11950 }
11951
11952 void LoadSetupFromFilename(char *filename)
11953 {
11954   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11955
11956   if (setup_file_hash)
11957   {
11958     decodeSetupFileHash_Default(setup_file_hash);
11959
11960     freeSetupFileHash(setup_file_hash);
11961   }
11962   else
11963   {
11964     Debug("setup", "using default setup values");
11965   }
11966 }
11967
11968 static void LoadSetup_SpecialPostProcessing(void)
11969 {
11970   char *player_name_new;
11971
11972   // needed to work around problems with fixed length strings
11973   player_name_new = getFixedUserName(setup.player_name);
11974   free(setup.player_name);
11975   setup.player_name = player_name_new;
11976
11977   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11978   if (setup.scroll_delay == FALSE)
11979   {
11980     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11981     setup.scroll_delay = TRUE;                  // now always "on"
11982   }
11983
11984   // make sure that scroll delay value stays inside valid range
11985   setup.scroll_delay_value =
11986     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11987 }
11988
11989 void LoadSetup_Default(void)
11990 {
11991   char *filename;
11992
11993   // always start with reliable default values
11994   setSetupInfoToDefaults(&setup);
11995
11996   // try to load setup values from default setup file
11997   filename = getDefaultSetupFilename();
11998
11999   if (fileExists(filename))
12000     LoadSetupFromFilename(filename);
12001
12002   // try to load setup values from platform setup file
12003   filename = getPlatformSetupFilename();
12004
12005   if (fileExists(filename))
12006     LoadSetupFromFilename(filename);
12007
12008   // try to load setup values from user setup file
12009   filename = getSetupFilename();
12010
12011   LoadSetupFromFilename(filename);
12012
12013   LoadSetup_SpecialPostProcessing();
12014 }
12015
12016 void LoadSetup_AutoSetup(void)
12017 {
12018   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12019   SetupFileHash *setup_file_hash = NULL;
12020
12021   // always start with reliable default values
12022   setSetupInfoToDefaults_AutoSetup(&setup);
12023
12024   setup_file_hash = loadSetupFileHash(filename);
12025
12026   if (setup_file_hash)
12027   {
12028     decodeSetupFileHash_AutoSetup(setup_file_hash);
12029
12030     freeSetupFileHash(setup_file_hash);
12031   }
12032
12033   free(filename);
12034 }
12035
12036 void LoadSetup_ServerSetup(void)
12037 {
12038   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12039   SetupFileHash *setup_file_hash = NULL;
12040
12041   // always start with reliable default values
12042   setSetupInfoToDefaults_ServerSetup(&setup);
12043
12044   setup_file_hash = loadSetupFileHash(filename);
12045
12046   if (setup_file_hash)
12047   {
12048     decodeSetupFileHash_ServerSetup(setup_file_hash);
12049
12050     freeSetupFileHash(setup_file_hash);
12051   }
12052
12053   free(filename);
12054
12055   if (setup.player_uuid == NULL)
12056   {
12057     // player UUID does not yet exist in setup file
12058     setup.player_uuid = getStringCopy(getUUID());
12059     setup.player_version = 2;
12060
12061     SaveSetup_ServerSetup();
12062   }
12063 }
12064
12065 void LoadSetup_EditorCascade(void)
12066 {
12067   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12068   SetupFileHash *setup_file_hash = NULL;
12069
12070   // always start with reliable default values
12071   setSetupInfoToDefaults_EditorCascade(&setup);
12072
12073   setup_file_hash = loadSetupFileHash(filename);
12074
12075   if (setup_file_hash)
12076   {
12077     decodeSetupFileHash_EditorCascade(setup_file_hash);
12078
12079     freeSetupFileHash(setup_file_hash);
12080   }
12081
12082   free(filename);
12083 }
12084
12085 void LoadSetup(void)
12086 {
12087   LoadSetup_Default();
12088   LoadSetup_AutoSetup();
12089   LoadSetup_ServerSetup();
12090   LoadSetup_EditorCascade();
12091 }
12092
12093 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12094                                            char *mapping_line)
12095 {
12096   char mapping_guid[MAX_LINE_LEN];
12097   char *mapping_start, *mapping_end;
12098
12099   // get GUID from game controller mapping line: copy complete line
12100   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12101   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12102
12103   // get GUID from game controller mapping line: cut after GUID part
12104   mapping_start = strchr(mapping_guid, ',');
12105   if (mapping_start != NULL)
12106     *mapping_start = '\0';
12107
12108   // cut newline from game controller mapping line
12109   mapping_end = strchr(mapping_line, '\n');
12110   if (mapping_end != NULL)
12111     *mapping_end = '\0';
12112
12113   // add mapping entry to game controller mappings hash
12114   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12115 }
12116
12117 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12118                                                  char *filename)
12119 {
12120   FILE *file;
12121
12122   if (!(file = fopen(filename, MODE_READ)))
12123   {
12124     Warn("cannot read game controller mappings file '%s'", filename);
12125
12126     return;
12127   }
12128
12129   while (!feof(file))
12130   {
12131     char line[MAX_LINE_LEN];
12132
12133     if (!fgets(line, MAX_LINE_LEN, file))
12134       break;
12135
12136     addGameControllerMappingToHash(mappings_hash, line);
12137   }
12138
12139   fclose(file);
12140 }
12141
12142 void SaveSetup_Default(void)
12143 {
12144   char *filename = getSetupFilename();
12145   FILE *file;
12146   int i, pnr;
12147
12148   InitUserDataDirectory();
12149
12150   if (!(file = fopen(filename, MODE_WRITE)))
12151   {
12152     Warn("cannot write setup file '%s'", filename);
12153
12154     return;
12155   }
12156
12157   fprintFileHeader(file, SETUP_FILENAME);
12158
12159   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12160   {
12161     // just to make things nicer :)
12162     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12163         global_setup_tokens[i].value == &setup.sound                    ||
12164         global_setup_tokens[i].value == &setup.graphics_set             ||
12165         global_setup_tokens[i].value == &setup.volume_simple            ||
12166         global_setup_tokens[i].value == &setup.network_mode             ||
12167         global_setup_tokens[i].value == &setup.touch.control_type       ||
12168         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12169         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12170       fprintf(file, "\n");
12171
12172     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12173   }
12174
12175   for (i = 0; i < 2; i++)
12176   {
12177     int grid_xsize = setup.touch.grid_xsize[i];
12178     int grid_ysize = setup.touch.grid_ysize[i];
12179     int x, y;
12180
12181     fprintf(file, "\n");
12182
12183     for (y = 0; y < grid_ysize; y++)
12184     {
12185       char token_string[MAX_LINE_LEN];
12186       char value_string[MAX_LINE_LEN];
12187
12188       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12189
12190       for (x = 0; x < grid_xsize; x++)
12191       {
12192         char c = setup.touch.grid_button[i][x][y];
12193
12194         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12195       }
12196
12197       value_string[grid_xsize] = '\0';
12198
12199       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12200     }
12201   }
12202
12203   fprintf(file, "\n");
12204   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12205     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12206
12207   fprintf(file, "\n");
12208   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12209     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12210
12211   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12212   {
12213     char prefix[30];
12214
12215     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12216     fprintf(file, "\n");
12217
12218     setup_input = setup.input[pnr];
12219     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12220       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12221   }
12222
12223   fprintf(file, "\n");
12224   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12225     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12226
12227   // (internal setup values not saved to user setup file)
12228
12229   fprintf(file, "\n");
12230   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12231     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12232         setup.debug.xsn_mode != AUTO)
12233       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12234
12235   fprintf(file, "\n");
12236   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12237     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12238
12239   fclose(file);
12240
12241   SetFilePermissions(filename, PERMS_PRIVATE);
12242 }
12243
12244 void SaveSetup_AutoSetup(void)
12245 {
12246   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12247   FILE *file;
12248   int i;
12249
12250   InitUserDataDirectory();
12251
12252   if (!(file = fopen(filename, MODE_WRITE)))
12253   {
12254     Warn("cannot write auto setup file '%s'", filename);
12255
12256     free(filename);
12257
12258     return;
12259   }
12260
12261   fprintFileHeader(file, AUTOSETUP_FILENAME);
12262
12263   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12264     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12265
12266   fclose(file);
12267
12268   SetFilePermissions(filename, PERMS_PRIVATE);
12269
12270   free(filename);
12271 }
12272
12273 void SaveSetup_ServerSetup(void)
12274 {
12275   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12276   FILE *file;
12277   int i;
12278
12279   InitUserDataDirectory();
12280
12281   if (!(file = fopen(filename, MODE_WRITE)))
12282   {
12283     Warn("cannot write server setup file '%s'", filename);
12284
12285     free(filename);
12286
12287     return;
12288   }
12289
12290   fprintFileHeader(file, SERVERSETUP_FILENAME);
12291
12292   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12293   {
12294     // just to make things nicer :)
12295     if (server_setup_tokens[i].value == &setup.use_api_server)
12296       fprintf(file, "\n");
12297
12298     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12299   }
12300
12301   fclose(file);
12302
12303   SetFilePermissions(filename, PERMS_PRIVATE);
12304
12305   free(filename);
12306 }
12307
12308 void SaveSetup_EditorCascade(void)
12309 {
12310   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12311   FILE *file;
12312   int i;
12313
12314   InitUserDataDirectory();
12315
12316   if (!(file = fopen(filename, MODE_WRITE)))
12317   {
12318     Warn("cannot write editor cascade state file '%s'", filename);
12319
12320     free(filename);
12321
12322     return;
12323   }
12324
12325   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12326
12327   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12328     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12329
12330   fclose(file);
12331
12332   SetFilePermissions(filename, PERMS_PRIVATE);
12333
12334   free(filename);
12335 }
12336
12337 void SaveSetup(void)
12338 {
12339   SaveSetup_Default();
12340   SaveSetup_AutoSetup();
12341   SaveSetup_ServerSetup();
12342   SaveSetup_EditorCascade();
12343 }
12344
12345 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12346                                                   char *filename)
12347 {
12348   FILE *file;
12349
12350   if (!(file = fopen(filename, MODE_WRITE)))
12351   {
12352     Warn("cannot write game controller mappings file '%s'", filename);
12353
12354     return;
12355   }
12356
12357   BEGIN_HASH_ITERATION(mappings_hash, itr)
12358   {
12359     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12360   }
12361   END_HASH_ITERATION(mappings_hash, itr)
12362
12363   fclose(file);
12364 }
12365
12366 void SaveSetup_AddGameControllerMapping(char *mapping)
12367 {
12368   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12369   SetupFileHash *mappings_hash = newSetupFileHash();
12370
12371   InitUserDataDirectory();
12372
12373   // load existing personal game controller mappings
12374   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12375
12376   // add new mapping to personal game controller mappings
12377   addGameControllerMappingToHash(mappings_hash, mapping);
12378
12379   // save updated personal game controller mappings
12380   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12381
12382   freeSetupFileHash(mappings_hash);
12383   free(filename);
12384 }
12385
12386 void LoadCustomElementDescriptions(void)
12387 {
12388   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12389   SetupFileHash *setup_file_hash;
12390   int i;
12391
12392   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12393   {
12394     if (element_info[i].custom_description != NULL)
12395     {
12396       free(element_info[i].custom_description);
12397       element_info[i].custom_description = NULL;
12398     }
12399   }
12400
12401   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12402     return;
12403
12404   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12405   {
12406     char *token = getStringCat2(element_info[i].token_name, ".name");
12407     char *value = getHashEntry(setup_file_hash, token);
12408
12409     if (value != NULL)
12410       element_info[i].custom_description = getStringCopy(value);
12411
12412     free(token);
12413   }
12414
12415   freeSetupFileHash(setup_file_hash);
12416 }
12417
12418 static int getElementFromToken(char *token)
12419 {
12420   char *value = getHashEntry(element_token_hash, token);
12421
12422   if (value != NULL)
12423     return atoi(value);
12424
12425   Warn("unknown element token '%s'", token);
12426
12427   return EL_UNDEFINED;
12428 }
12429
12430 void FreeGlobalAnimEventInfo(void)
12431 {
12432   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12433
12434   if (gaei->event_list == NULL)
12435     return;
12436
12437   int i;
12438
12439   for (i = 0; i < gaei->num_event_lists; i++)
12440   {
12441     checked_free(gaei->event_list[i]->event_value);
12442     checked_free(gaei->event_list[i]);
12443   }
12444
12445   checked_free(gaei->event_list);
12446
12447   gaei->event_list = NULL;
12448   gaei->num_event_lists = 0;
12449 }
12450
12451 static int AddGlobalAnimEventList(void)
12452 {
12453   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12454   int list_pos = gaei->num_event_lists++;
12455
12456   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12457                                      sizeof(struct GlobalAnimEventListInfo *));
12458
12459   gaei->event_list[list_pos] =
12460     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12461
12462   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12463
12464   gaeli->event_value = NULL;
12465   gaeli->num_event_values = 0;
12466
12467   return list_pos;
12468 }
12469
12470 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12471 {
12472   // do not add empty global animation events
12473   if (event_value == ANIM_EVENT_NONE)
12474     return list_pos;
12475
12476   // if list position is undefined, create new list
12477   if (list_pos == ANIM_EVENT_UNDEFINED)
12478     list_pos = AddGlobalAnimEventList();
12479
12480   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12481   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12482   int value_pos = gaeli->num_event_values++;
12483
12484   gaeli->event_value = checked_realloc(gaeli->event_value,
12485                                        gaeli->num_event_values * sizeof(int *));
12486
12487   gaeli->event_value[value_pos] = event_value;
12488
12489   return list_pos;
12490 }
12491
12492 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12493 {
12494   if (list_pos == ANIM_EVENT_UNDEFINED)
12495     return ANIM_EVENT_NONE;
12496
12497   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12498   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12499
12500   return gaeli->event_value[value_pos];
12501 }
12502
12503 int GetGlobalAnimEventValueCount(int list_pos)
12504 {
12505   if (list_pos == ANIM_EVENT_UNDEFINED)
12506     return 0;
12507
12508   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12509   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12510
12511   return gaeli->num_event_values;
12512 }
12513
12514 // This function checks if a string <s> of the format "string1, string2, ..."
12515 // exactly contains a string <s_contained>.
12516
12517 static boolean string_has_parameter(char *s, char *s_contained)
12518 {
12519   char *substring;
12520
12521   if (s == NULL || s_contained == NULL)
12522     return FALSE;
12523
12524   if (strlen(s_contained) > strlen(s))
12525     return FALSE;
12526
12527   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12528   {
12529     char next_char = s[strlen(s_contained)];
12530
12531     // check if next character is delimiter or whitespace
12532     if (next_char == ',' || next_char == '\0' ||
12533         next_char == ' ' || next_char == '\t')
12534       return TRUE;
12535   }
12536
12537   // check if string contains another parameter string after a comma
12538   substring = strchr(s, ',');
12539   if (substring == NULL)        // string does not contain a comma
12540     return FALSE;
12541
12542   // advance string pointer to next character after the comma
12543   substring++;
12544
12545   // skip potential whitespaces after the comma
12546   while (*substring == ' ' || *substring == '\t')
12547     substring++;
12548
12549   return string_has_parameter(substring, s_contained);
12550 }
12551
12552 static int get_anim_parameter_value_ce(char *s)
12553 {
12554   char *s_ptr = s;
12555   char *pattern_1 = "ce_change:custom_";
12556   char *pattern_2 = ".page_";
12557   int pattern_1_len = strlen(pattern_1);
12558   char *matching_char = strstr(s_ptr, pattern_1);
12559   int result = ANIM_EVENT_NONE;
12560
12561   if (matching_char == NULL)
12562     return ANIM_EVENT_NONE;
12563
12564   result = ANIM_EVENT_CE_CHANGE;
12565
12566   s_ptr = matching_char + pattern_1_len;
12567
12568   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12569   if (*s_ptr >= '0' && *s_ptr <= '9')
12570   {
12571     int gic_ce_nr = (*s_ptr++ - '0');
12572
12573     if (*s_ptr >= '0' && *s_ptr <= '9')
12574     {
12575       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12576
12577       if (*s_ptr >= '0' && *s_ptr <= '9')
12578         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12579     }
12580
12581     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12582       return ANIM_EVENT_NONE;
12583
12584     // custom element stored as 0 to 255
12585     gic_ce_nr--;
12586
12587     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12588   }
12589   else
12590   {
12591     // invalid custom element number specified
12592
12593     return ANIM_EVENT_NONE;
12594   }
12595
12596   // check for change page number ("page_X" or "page_XX") (optional)
12597   if (strPrefix(s_ptr, pattern_2))
12598   {
12599     s_ptr += strlen(pattern_2);
12600
12601     if (*s_ptr >= '0' && *s_ptr <= '9')
12602     {
12603       int gic_page_nr = (*s_ptr++ - '0');
12604
12605       if (*s_ptr >= '0' && *s_ptr <= '9')
12606         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12607
12608       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12609         return ANIM_EVENT_NONE;
12610
12611       // change page stored as 1 to 32 (0 means "all change pages")
12612
12613       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12614     }
12615     else
12616     {
12617       // invalid animation part number specified
12618
12619       return ANIM_EVENT_NONE;
12620     }
12621   }
12622
12623   // discard result if next character is neither delimiter nor whitespace
12624   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12625         *s_ptr == ' ' || *s_ptr == '\t'))
12626     return ANIM_EVENT_NONE;
12627
12628   return result;
12629 }
12630
12631 static int get_anim_parameter_value(char *s)
12632 {
12633   int event_value[] =
12634   {
12635     ANIM_EVENT_CLICK,
12636     ANIM_EVENT_INIT,
12637     ANIM_EVENT_START,
12638     ANIM_EVENT_END,
12639     ANIM_EVENT_POST
12640   };
12641   char *pattern_1[] =
12642   {
12643     "click:anim_",
12644     "init:anim_",
12645     "start:anim_",
12646     "end:anim_",
12647     "post:anim_"
12648   };
12649   char *pattern_2 = ".part_";
12650   char *matching_char = NULL;
12651   char *s_ptr = s;
12652   int pattern_1_len = 0;
12653   int result = ANIM_EVENT_NONE;
12654   int i;
12655
12656   result = get_anim_parameter_value_ce(s);
12657
12658   if (result != ANIM_EVENT_NONE)
12659     return result;
12660
12661   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12662   {
12663     matching_char = strstr(s_ptr, pattern_1[i]);
12664     pattern_1_len = strlen(pattern_1[i]);
12665     result = event_value[i];
12666
12667     if (matching_char != NULL)
12668       break;
12669   }
12670
12671   if (matching_char == NULL)
12672     return ANIM_EVENT_NONE;
12673
12674   s_ptr = matching_char + pattern_1_len;
12675
12676   // check for main animation number ("anim_X" or "anim_XX")
12677   if (*s_ptr >= '0' && *s_ptr <= '9')
12678   {
12679     int gic_anim_nr = (*s_ptr++ - '0');
12680
12681     if (*s_ptr >= '0' && *s_ptr <= '9')
12682       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12683
12684     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12685       return ANIM_EVENT_NONE;
12686
12687     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12688   }
12689   else
12690   {
12691     // invalid main animation number specified
12692
12693     return ANIM_EVENT_NONE;
12694   }
12695
12696   // check for animation part number ("part_X" or "part_XX") (optional)
12697   if (strPrefix(s_ptr, pattern_2))
12698   {
12699     s_ptr += strlen(pattern_2);
12700
12701     if (*s_ptr >= '0' && *s_ptr <= '9')
12702     {
12703       int gic_part_nr = (*s_ptr++ - '0');
12704
12705       if (*s_ptr >= '0' && *s_ptr <= '9')
12706         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12707
12708       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12709         return ANIM_EVENT_NONE;
12710
12711       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12712     }
12713     else
12714     {
12715       // invalid animation part number specified
12716
12717       return ANIM_EVENT_NONE;
12718     }
12719   }
12720
12721   // discard result if next character is neither delimiter nor whitespace
12722   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12723         *s_ptr == ' ' || *s_ptr == '\t'))
12724     return ANIM_EVENT_NONE;
12725
12726   return result;
12727 }
12728
12729 static int get_anim_parameter_values(char *s)
12730 {
12731   int list_pos = ANIM_EVENT_UNDEFINED;
12732   int event_value = ANIM_EVENT_DEFAULT;
12733
12734   if (string_has_parameter(s, "any"))
12735     event_value |= ANIM_EVENT_ANY;
12736
12737   if (string_has_parameter(s, "click:self") ||
12738       string_has_parameter(s, "click") ||
12739       string_has_parameter(s, "self"))
12740     event_value |= ANIM_EVENT_SELF;
12741
12742   if (string_has_parameter(s, "unclick:any"))
12743     event_value |= ANIM_EVENT_UNCLICK_ANY;
12744
12745   // if animation event found, add it to global animation event list
12746   if (event_value != ANIM_EVENT_NONE)
12747     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12748
12749   while (s != NULL)
12750   {
12751     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12752     event_value = get_anim_parameter_value(s);
12753
12754     // if animation event found, add it to global animation event list
12755     if (event_value != ANIM_EVENT_NONE)
12756       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12757
12758     // continue with next part of the string, starting with next comma
12759     s = strchr(s + 1, ',');
12760   }
12761
12762   return list_pos;
12763 }
12764
12765 static int get_anim_action_parameter_value(char *token)
12766 {
12767   // check most common default case first to massively speed things up
12768   if (strEqual(token, ARG_UNDEFINED))
12769     return ANIM_EVENT_ACTION_NONE;
12770
12771   int result = getImageIDFromToken(token);
12772
12773   if (result == -1)
12774   {
12775     char *gfx_token = getStringCat2("gfx.", token);
12776
12777     result = getImageIDFromToken(gfx_token);
12778
12779     checked_free(gfx_token);
12780   }
12781
12782   if (result == -1)
12783   {
12784     Key key = getKeyFromX11KeyName(token);
12785
12786     if (key != KSYM_UNDEFINED)
12787       result = -(int)key;
12788   }
12789
12790   if (result == -1)
12791   {
12792     if (isURL(token))
12793     {
12794       result = get_hash_from_string(token);     // unsigned int => int
12795       result = ABS(result);                     // may be negative now
12796       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12797
12798       setHashEntry(anim_url_hash, int2str(result, 0), token);
12799     }
12800   }
12801
12802   if (result == -1)
12803     result = ANIM_EVENT_ACTION_NONE;
12804
12805   return result;
12806 }
12807
12808 int get_parameter_value(char *value_raw, char *suffix, int type)
12809 {
12810   char *value = getStringToLower(value_raw);
12811   int result = 0;       // probably a save default value
12812
12813   if (strEqual(suffix, ".direction"))
12814   {
12815     result = (strEqual(value, "left")  ? MV_LEFT :
12816               strEqual(value, "right") ? MV_RIGHT :
12817               strEqual(value, "up")    ? MV_UP :
12818               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12819   }
12820   else if (strEqual(suffix, ".position"))
12821   {
12822     result = (strEqual(value, "left")   ? POS_LEFT :
12823               strEqual(value, "right")  ? POS_RIGHT :
12824               strEqual(value, "top")    ? POS_TOP :
12825               strEqual(value, "upper")  ? POS_UPPER :
12826               strEqual(value, "middle") ? POS_MIDDLE :
12827               strEqual(value, "lower")  ? POS_LOWER :
12828               strEqual(value, "bottom") ? POS_BOTTOM :
12829               strEqual(value, "any")    ? POS_ANY :
12830               strEqual(value, "ce")     ? POS_CE :
12831               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12832               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12833   }
12834   else if (strEqual(suffix, ".align"))
12835   {
12836     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12837               strEqual(value, "right")  ? ALIGN_RIGHT :
12838               strEqual(value, "center") ? ALIGN_CENTER :
12839               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12840   }
12841   else if (strEqual(suffix, ".valign"))
12842   {
12843     result = (strEqual(value, "top")    ? VALIGN_TOP :
12844               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12845               strEqual(value, "middle") ? VALIGN_MIDDLE :
12846               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12847   }
12848   else if (strEqual(suffix, ".anim_mode"))
12849   {
12850     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12851               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12852               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12853               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12854               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12855               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12856               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12857               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12858               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12859               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12860               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12861               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12862               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12863               string_has_parameter(value, "all")        ? ANIM_ALL :
12864               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12865               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12866               ANIM_DEFAULT);
12867
12868     if (string_has_parameter(value, "once"))
12869       result |= ANIM_ONCE;
12870
12871     if (string_has_parameter(value, "reverse"))
12872       result |= ANIM_REVERSE;
12873
12874     if (string_has_parameter(value, "opaque_player"))
12875       result |= ANIM_OPAQUE_PLAYER;
12876
12877     if (string_has_parameter(value, "static_panel"))
12878       result |= ANIM_STATIC_PANEL;
12879   }
12880   else if (strEqual(suffix, ".init_event") ||
12881            strEqual(suffix, ".anim_event"))
12882   {
12883     result = get_anim_parameter_values(value);
12884   }
12885   else if (strEqual(suffix, ".init_delay_action") ||
12886            strEqual(suffix, ".anim_delay_action") ||
12887            strEqual(suffix, ".post_delay_action") ||
12888            strEqual(suffix, ".init_event_action") ||
12889            strEqual(suffix, ".anim_event_action"))
12890   {
12891     result = get_anim_action_parameter_value(value_raw);
12892   }
12893   else if (strEqual(suffix, ".class"))
12894   {
12895     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12896               get_hash_from_string(value));
12897   }
12898   else if (strEqual(suffix, ".style"))
12899   {
12900     result = STYLE_DEFAULT;
12901
12902     if (string_has_parameter(value, "accurate_borders"))
12903       result |= STYLE_ACCURATE_BORDERS;
12904
12905     if (string_has_parameter(value, "inner_corners"))
12906       result |= STYLE_INNER_CORNERS;
12907
12908     if (string_has_parameter(value, "reverse"))
12909       result |= STYLE_REVERSE;
12910
12911     if (string_has_parameter(value, "leftmost_position"))
12912       result |= STYLE_LEFTMOST_POSITION;
12913
12914     if (string_has_parameter(value, "block_clicks"))
12915       result |= STYLE_BLOCK;
12916
12917     if (string_has_parameter(value, "passthrough_clicks"))
12918       result |= STYLE_PASSTHROUGH;
12919
12920     if (string_has_parameter(value, "multiple_actions"))
12921       result |= STYLE_MULTIPLE_ACTIONS;
12922
12923     if (string_has_parameter(value, "consume_ce_event"))
12924       result |= STYLE_CONSUME_CE_EVENT;
12925   }
12926   else if (strEqual(suffix, ".fade_mode"))
12927   {
12928     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12929               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12930               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12931               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12932               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12933               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12934               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12935               FADE_MODE_DEFAULT);
12936   }
12937   else if (strEqual(suffix, ".auto_delay_unit"))
12938   {
12939     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12940               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12941               AUTO_DELAY_UNIT_DEFAULT);
12942   }
12943   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12944   {
12945     result = gfx.get_font_from_token_function(value);
12946   }
12947   else          // generic parameter of type integer or boolean
12948   {
12949     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12950               type == TYPE_INTEGER ? get_integer_from_string(value) :
12951               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12952               ARG_UNDEFINED_VALUE);
12953   }
12954
12955   free(value);
12956
12957   return result;
12958 }
12959
12960 static int get_token_parameter_value(char *token, char *value_raw)
12961 {
12962   char *suffix;
12963
12964   if (token == NULL || value_raw == NULL)
12965     return ARG_UNDEFINED_VALUE;
12966
12967   suffix = strrchr(token, '.');
12968   if (suffix == NULL)
12969     suffix = token;
12970
12971   if (strEqual(suffix, ".element"))
12972     return getElementFromToken(value_raw);
12973
12974   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12975   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12976 }
12977
12978 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12979                                      boolean ignore_defaults)
12980 {
12981   int i;
12982
12983   for (i = 0; image_config_vars[i].token != NULL; i++)
12984   {
12985     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12986
12987     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12988     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12989       continue;
12990
12991     if (value != NULL)
12992       *image_config_vars[i].value =
12993         get_token_parameter_value(image_config_vars[i].token, value);
12994   }
12995 }
12996
12997 void InitMenuDesignSettings_Static(void)
12998 {
12999   // always start with reliable default values from static default config
13000   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13001 }
13002
13003 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13004 {
13005   int i;
13006
13007   // the following initializes hierarchical values from static configuration
13008
13009   // special case: initialize "ARG_DEFAULT" values in static default config
13010   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13011   titlescreen_initial_first_default.fade_mode  =
13012     title_initial_first_default.fade_mode;
13013   titlescreen_initial_first_default.fade_delay =
13014     title_initial_first_default.fade_delay;
13015   titlescreen_initial_first_default.post_delay =
13016     title_initial_first_default.post_delay;
13017   titlescreen_initial_first_default.auto_delay =
13018     title_initial_first_default.auto_delay;
13019   titlescreen_initial_first_default.auto_delay_unit =
13020     title_initial_first_default.auto_delay_unit;
13021   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13022   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13023   titlescreen_first_default.post_delay = title_first_default.post_delay;
13024   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13025   titlescreen_first_default.auto_delay_unit =
13026     title_first_default.auto_delay_unit;
13027   titlemessage_initial_first_default.fade_mode  =
13028     title_initial_first_default.fade_mode;
13029   titlemessage_initial_first_default.fade_delay =
13030     title_initial_first_default.fade_delay;
13031   titlemessage_initial_first_default.post_delay =
13032     title_initial_first_default.post_delay;
13033   titlemessage_initial_first_default.auto_delay =
13034     title_initial_first_default.auto_delay;
13035   titlemessage_initial_first_default.auto_delay_unit =
13036     title_initial_first_default.auto_delay_unit;
13037   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13038   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13039   titlemessage_first_default.post_delay = title_first_default.post_delay;
13040   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13041   titlemessage_first_default.auto_delay_unit =
13042     title_first_default.auto_delay_unit;
13043
13044   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13045   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13046   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13047   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13048   titlescreen_initial_default.auto_delay_unit =
13049     title_initial_default.auto_delay_unit;
13050   titlescreen_default.fade_mode  = title_default.fade_mode;
13051   titlescreen_default.fade_delay = title_default.fade_delay;
13052   titlescreen_default.post_delay = title_default.post_delay;
13053   titlescreen_default.auto_delay = title_default.auto_delay;
13054   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13055   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13056   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13057   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13058   titlemessage_initial_default.auto_delay_unit =
13059     title_initial_default.auto_delay_unit;
13060   titlemessage_default.fade_mode  = title_default.fade_mode;
13061   titlemessage_default.fade_delay = title_default.fade_delay;
13062   titlemessage_default.post_delay = title_default.post_delay;
13063   titlemessage_default.auto_delay = title_default.auto_delay;
13064   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13065
13066   // special case: initialize "ARG_DEFAULT" values in static default config
13067   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13068   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13069   {
13070     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13071     titlescreen_first[i] = titlescreen_first_default;
13072     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13073     titlemessage_first[i] = titlemessage_first_default;
13074
13075     titlescreen_initial[i] = titlescreen_initial_default;
13076     titlescreen[i] = titlescreen_default;
13077     titlemessage_initial[i] = titlemessage_initial_default;
13078     titlemessage[i] = titlemessage_default;
13079   }
13080
13081   // special case: initialize "ARG_DEFAULT" values in static default config
13082   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13083   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13084   {
13085     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13086       continue;
13087
13088     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13089     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13090     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13091   }
13092
13093   // special case: initialize "ARG_DEFAULT" values in static default config
13094   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13095   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13096   {
13097     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13098     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13099     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13100
13101     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13102       continue;
13103
13104     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13105   }
13106 }
13107
13108 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13109 {
13110   static struct
13111   {
13112     struct XY *dst, *src;
13113   }
13114   game_buttons_xy[] =
13115   {
13116     { &game.button.save,        &game.button.stop       },
13117     { &game.button.pause2,      &game.button.pause      },
13118     { &game.button.load,        &game.button.play       },
13119     { &game.button.undo,        &game.button.stop       },
13120     { &game.button.redo,        &game.button.play       },
13121
13122     { NULL,                     NULL                    }
13123   };
13124   int i, j;
13125
13126   // special case: initialize later added SETUP list size from LEVELS value
13127   if (menu.list_size[GAME_MODE_SETUP] == -1)
13128     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13129
13130   // set default position for snapshot buttons to stop/pause/play buttons
13131   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13132     if ((*game_buttons_xy[i].dst).x == -1 &&
13133         (*game_buttons_xy[i].dst).y == -1)
13134       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13135
13136   // --------------------------------------------------------------------------
13137   // dynamic viewports (including playfield margins, borders and alignments)
13138   // --------------------------------------------------------------------------
13139
13140   // dynamic viewports currently only supported for landscape mode
13141   int display_width  = MAX(video.display_width, video.display_height);
13142   int display_height = MIN(video.display_width, video.display_height);
13143
13144   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13145   {
13146     struct RectWithBorder *vp_window    = &viewport.window[i];
13147     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13148     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13149     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13150     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13151     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13152     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13153     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13154
13155     // adjust window size if min/max width/height is specified
13156
13157     if (vp_window->min_width != -1)
13158     {
13159       int window_width = display_width;
13160
13161       // when using static window height, use aspect ratio of display
13162       if (vp_window->min_height == -1)
13163         window_width = vp_window->height * display_width / display_height;
13164
13165       vp_window->width = MAX(vp_window->min_width, window_width);
13166     }
13167
13168     if (vp_window->min_height != -1)
13169     {
13170       int window_height = display_height;
13171
13172       // when using static window width, use aspect ratio of display
13173       if (vp_window->min_width == -1)
13174         window_height = vp_window->width * display_height / display_width;
13175
13176       vp_window->height = MAX(vp_window->min_height, window_height);
13177     }
13178
13179     if (vp_window->max_width != -1)
13180       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13181
13182     if (vp_window->max_height != -1)
13183       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13184
13185     int playfield_width  = vp_window->width;
13186     int playfield_height = vp_window->height;
13187
13188     // adjust playfield size and position according to specified margins
13189
13190     playfield_width  -= vp_playfield->margin_left;
13191     playfield_width  -= vp_playfield->margin_right;
13192
13193     playfield_height -= vp_playfield->margin_top;
13194     playfield_height -= vp_playfield->margin_bottom;
13195
13196     // adjust playfield size if min/max width/height is specified
13197
13198     if (vp_playfield->min_width != -1)
13199       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13200
13201     if (vp_playfield->min_height != -1)
13202       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13203
13204     if (vp_playfield->max_width != -1)
13205       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13206
13207     if (vp_playfield->max_height != -1)
13208       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13209
13210     // adjust playfield position according to specified alignment
13211
13212     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13213       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13214     else if (vp_playfield->align == ALIGN_CENTER)
13215       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13216     else if (vp_playfield->align == ALIGN_RIGHT)
13217       vp_playfield->x += playfield_width - vp_playfield->width;
13218
13219     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13220       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13221     else if (vp_playfield->valign == VALIGN_MIDDLE)
13222       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13223     else if (vp_playfield->valign == VALIGN_BOTTOM)
13224       vp_playfield->y += playfield_height - vp_playfield->height;
13225
13226     vp_playfield->x += vp_playfield->margin_left;
13227     vp_playfield->y += vp_playfield->margin_top;
13228
13229     // adjust individual playfield borders if only default border is specified
13230
13231     if (vp_playfield->border_left == -1)
13232       vp_playfield->border_left = vp_playfield->border_size;
13233     if (vp_playfield->border_right == -1)
13234       vp_playfield->border_right = vp_playfield->border_size;
13235     if (vp_playfield->border_top == -1)
13236       vp_playfield->border_top = vp_playfield->border_size;
13237     if (vp_playfield->border_bottom == -1)
13238       vp_playfield->border_bottom = vp_playfield->border_size;
13239
13240     // set dynamic playfield borders if borders are specified as undefined
13241     // (but only if window size was dynamic and playfield size was static)
13242
13243     if (dynamic_window_width && !dynamic_playfield_width)
13244     {
13245       if (vp_playfield->border_left == -1)
13246       {
13247         vp_playfield->border_left = (vp_playfield->x -
13248                                      vp_playfield->margin_left);
13249         vp_playfield->x     -= vp_playfield->border_left;
13250         vp_playfield->width += vp_playfield->border_left;
13251       }
13252
13253       if (vp_playfield->border_right == -1)
13254       {
13255         vp_playfield->border_right = (vp_window->width -
13256                                       vp_playfield->x -
13257                                       vp_playfield->width -
13258                                       vp_playfield->margin_right);
13259         vp_playfield->width += vp_playfield->border_right;
13260       }
13261     }
13262
13263     if (dynamic_window_height && !dynamic_playfield_height)
13264     {
13265       if (vp_playfield->border_top == -1)
13266       {
13267         vp_playfield->border_top = (vp_playfield->y -
13268                                     vp_playfield->margin_top);
13269         vp_playfield->y      -= vp_playfield->border_top;
13270         vp_playfield->height += vp_playfield->border_top;
13271       }
13272
13273       if (vp_playfield->border_bottom == -1)
13274       {
13275         vp_playfield->border_bottom = (vp_window->height -
13276                                        vp_playfield->y -
13277                                        vp_playfield->height -
13278                                        vp_playfield->margin_bottom);
13279         vp_playfield->height += vp_playfield->border_bottom;
13280       }
13281     }
13282
13283     // adjust playfield size to be a multiple of a defined alignment tile size
13284
13285     int align_size = vp_playfield->align_size;
13286     int playfield_xtiles = vp_playfield->width  / align_size;
13287     int playfield_ytiles = vp_playfield->height / align_size;
13288     int playfield_width_corrected  = playfield_xtiles * align_size;
13289     int playfield_height_corrected = playfield_ytiles * align_size;
13290     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13291                                  i == GFX_SPECIAL_ARG_EDITOR);
13292
13293     if (is_playfield_mode &&
13294         dynamic_playfield_width &&
13295         vp_playfield->width != playfield_width_corrected)
13296     {
13297       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13298
13299       vp_playfield->width = playfield_width_corrected;
13300
13301       if (vp_playfield->align == ALIGN_LEFT)
13302       {
13303         vp_playfield->border_left += playfield_xdiff;
13304       }
13305       else if (vp_playfield->align == ALIGN_RIGHT)
13306       {
13307         vp_playfield->border_right += playfield_xdiff;
13308       }
13309       else if (vp_playfield->align == ALIGN_CENTER)
13310       {
13311         int border_left_diff  = playfield_xdiff / 2;
13312         int border_right_diff = playfield_xdiff - border_left_diff;
13313
13314         vp_playfield->border_left  += border_left_diff;
13315         vp_playfield->border_right += border_right_diff;
13316       }
13317     }
13318
13319     if (is_playfield_mode &&
13320         dynamic_playfield_height &&
13321         vp_playfield->height != playfield_height_corrected)
13322     {
13323       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13324
13325       vp_playfield->height = playfield_height_corrected;
13326
13327       if (vp_playfield->valign == VALIGN_TOP)
13328       {
13329         vp_playfield->border_top += playfield_ydiff;
13330       }
13331       else if (vp_playfield->align == VALIGN_BOTTOM)
13332       {
13333         vp_playfield->border_right += playfield_ydiff;
13334       }
13335       else if (vp_playfield->align == VALIGN_MIDDLE)
13336       {
13337         int border_top_diff    = playfield_ydiff / 2;
13338         int border_bottom_diff = playfield_ydiff - border_top_diff;
13339
13340         vp_playfield->border_top    += border_top_diff;
13341         vp_playfield->border_bottom += border_bottom_diff;
13342       }
13343     }
13344
13345     // adjust door positions according to specified alignment
13346
13347     for (j = 0; j < 2; j++)
13348     {
13349       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13350
13351       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13352         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13353       else if (vp_door->align == ALIGN_CENTER)
13354         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13355       else if (vp_door->align == ALIGN_RIGHT)
13356         vp_door->x += vp_window->width - vp_door->width;
13357
13358       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13359         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13360       else if (vp_door->valign == VALIGN_MIDDLE)
13361         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13362       else if (vp_door->valign == VALIGN_BOTTOM)
13363         vp_door->y += vp_window->height - vp_door->height;
13364     }
13365   }
13366 }
13367
13368 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13369 {
13370   static struct
13371   {
13372     struct XYTileSize *dst, *src;
13373     int graphic;
13374   }
13375   editor_buttons_xy[] =
13376   {
13377     {
13378       &editor.button.element_left,      &editor.palette.element_left,
13379       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13380     },
13381     {
13382       &editor.button.element_middle,    &editor.palette.element_middle,
13383       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13384     },
13385     {
13386       &editor.button.element_right,     &editor.palette.element_right,
13387       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13388     },
13389
13390     { NULL,                     NULL                    }
13391   };
13392   int i;
13393
13394   // set default position for element buttons to element graphics
13395   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13396   {
13397     if ((*editor_buttons_xy[i].dst).x == -1 &&
13398         (*editor_buttons_xy[i].dst).y == -1)
13399     {
13400       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13401
13402       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13403
13404       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13405     }
13406   }
13407
13408   // adjust editor palette rows and columns if specified to be dynamic
13409
13410   if (editor.palette.cols == -1)
13411   {
13412     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13413     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13414     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13415
13416     editor.palette.cols = (vp_width - sc_width) / bt_width;
13417
13418     if (editor.palette.x == -1)
13419     {
13420       int palette_width = editor.palette.cols * bt_width + sc_width;
13421
13422       editor.palette.x = (vp_width - palette_width) / 2;
13423     }
13424   }
13425
13426   if (editor.palette.rows == -1)
13427   {
13428     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13429     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13430     int tx_height = getFontHeight(FONT_TEXT_2);
13431
13432     editor.palette.rows = (vp_height - tx_height) / bt_height;
13433
13434     if (editor.palette.y == -1)
13435     {
13436       int palette_height = editor.palette.rows * bt_height + tx_height;
13437
13438       editor.palette.y = (vp_height - palette_height) / 2;
13439     }
13440   }
13441 }
13442
13443 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13444                                                       boolean initialize)
13445 {
13446   // special case: check if network and preview player positions are redefined,
13447   // to compare this later against the main menu level preview being redefined
13448   struct TokenIntPtrInfo menu_config_players[] =
13449   {
13450     { "main.network_players.x", &menu.main.network_players.redefined    },
13451     { "main.network_players.y", &menu.main.network_players.redefined    },
13452     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13453     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13454     { "preview.x",              &preview.redefined                      },
13455     { "preview.y",              &preview.redefined                      }
13456   };
13457   int i;
13458
13459   if (initialize)
13460   {
13461     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13462       *menu_config_players[i].value = FALSE;
13463   }
13464   else
13465   {
13466     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13467       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13468         *menu_config_players[i].value = TRUE;
13469   }
13470 }
13471
13472 static void InitMenuDesignSettings_PreviewPlayers(void)
13473 {
13474   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13475 }
13476
13477 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13478 {
13479   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13480 }
13481
13482 static void LoadMenuDesignSettingsFromFilename(char *filename)
13483 {
13484   static struct TitleFadingInfo tfi;
13485   static struct TitleMessageInfo tmi;
13486   static struct TokenInfo title_tokens[] =
13487   {
13488     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13489     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13490     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13491     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13492     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13493
13494     { -1,               NULL,                   NULL                    }
13495   };
13496   static struct TokenInfo titlemessage_tokens[] =
13497   {
13498     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13499     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13500     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13501     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13502     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13503     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13504     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13505     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13506     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13507     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13508     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13509     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13510     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13511     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13512     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13513     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13514     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13515     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13516
13517     { -1,               NULL,                   NULL                    }
13518   };
13519   static struct
13520   {
13521     struct TitleFadingInfo *info;
13522     char *text;
13523   }
13524   title_info[] =
13525   {
13526     // initialize first titles from "enter screen" definitions, if defined
13527     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13528     { &title_first_default,             "menu.enter_screen.TITLE"       },
13529
13530     // initialize title screens from "next screen" definitions, if defined
13531     { &title_initial_default,           "menu.next_screen.TITLE"        },
13532     { &title_default,                   "menu.next_screen.TITLE"        },
13533
13534     { NULL,                             NULL                            }
13535   };
13536   static struct
13537   {
13538     struct TitleMessageInfo *array;
13539     char *text;
13540   }
13541   titlemessage_arrays[] =
13542   {
13543     // initialize first titles from "enter screen" definitions, if defined
13544     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13545     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13546     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13547     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13548
13549     // initialize titles from "next screen" definitions, if defined
13550     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13551     { titlescreen,                      "menu.next_screen.TITLE"        },
13552     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13553     { titlemessage,                     "menu.next_screen.TITLE"        },
13554
13555     // overwrite titles with title definitions, if defined
13556     { titlescreen_initial_first,        "[title_initial]"               },
13557     { titlescreen_first,                "[title]"                       },
13558     { titlemessage_initial_first,       "[title_initial]"               },
13559     { titlemessage_first,               "[title]"                       },
13560
13561     { titlescreen_initial,              "[title_initial]"               },
13562     { titlescreen,                      "[title]"                       },
13563     { titlemessage_initial,             "[title_initial]"               },
13564     { titlemessage,                     "[title]"                       },
13565
13566     // overwrite titles with title screen/message definitions, if defined
13567     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13568     { titlescreen_first,                "[titlescreen]"                 },
13569     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13570     { titlemessage_first,               "[titlemessage]"                },
13571
13572     { titlescreen_initial,              "[titlescreen_initial]"         },
13573     { titlescreen,                      "[titlescreen]"                 },
13574     { titlemessage_initial,             "[titlemessage_initial]"        },
13575     { titlemessage,                     "[titlemessage]"                },
13576
13577     { NULL,                             NULL                            }
13578   };
13579   SetupFileHash *setup_file_hash;
13580   int i, j, k;
13581
13582   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13583     return;
13584
13585   // the following initializes hierarchical values from dynamic configuration
13586
13587   // special case: initialize with default values that may be overwritten
13588   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13589   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13590   {
13591     struct TokenIntPtrInfo menu_config[] =
13592     {
13593       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13594       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13595       { "menu.list_size",       &menu.list_size[i]      }
13596     };
13597
13598     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13599     {
13600       char *token = menu_config[j].token;
13601       char *value = getHashEntry(setup_file_hash, token);
13602
13603       if (value != NULL)
13604         *menu_config[j].value = get_integer_from_string(value);
13605     }
13606   }
13607
13608   // special case: initialize with default values that may be overwritten
13609   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13610   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13611   {
13612     struct TokenIntPtrInfo menu_config[] =
13613     {
13614       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13615       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13616       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13617       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13618       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13619     };
13620
13621     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13622     {
13623       char *token = menu_config[j].token;
13624       char *value = getHashEntry(setup_file_hash, token);
13625
13626       if (value != NULL)
13627         *menu_config[j].value = get_integer_from_string(value);
13628     }
13629   }
13630
13631   // special case: initialize with default values that may be overwritten
13632   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13633   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13634   {
13635     struct TokenIntPtrInfo menu_config[] =
13636     {
13637       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13638       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13639     };
13640
13641     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13642     {
13643       char *token = menu_config[j].token;
13644       char *value = getHashEntry(setup_file_hash, token);
13645
13646       if (value != NULL)
13647         *menu_config[j].value = get_integer_from_string(value);
13648     }
13649   }
13650
13651   // special case: initialize with default values that may be overwritten
13652   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13653   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13654   {
13655     struct TokenIntPtrInfo menu_config[] =
13656     {
13657       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13658       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13659       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13660       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13661       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13662       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13663       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13664       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13665       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13666       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13667     };
13668
13669     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13670     {
13671       char *token = menu_config[j].token;
13672       char *value = getHashEntry(setup_file_hash, token);
13673
13674       if (value != NULL)
13675         *menu_config[j].value = get_integer_from_string(value);
13676     }
13677   }
13678
13679   // special case: initialize with default values that may be overwritten
13680   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13681   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13682   {
13683     struct TokenIntPtrInfo menu_config[] =
13684     {
13685       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13686       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13687       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13688       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13689       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13690       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13691       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13692       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13693       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13694     };
13695
13696     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13697     {
13698       char *token = menu_config[j].token;
13699       char *value = getHashEntry(setup_file_hash, token);
13700
13701       if (value != NULL)
13702         *menu_config[j].value = get_token_parameter_value(token, value);
13703     }
13704   }
13705
13706   // special case: initialize with default values that may be overwritten
13707   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13708   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13709   {
13710     struct
13711     {
13712       char *token_prefix;
13713       struct RectWithBorder *struct_ptr;
13714     }
13715     vp_struct[] =
13716     {
13717       { "viewport.window",      &viewport.window[i]     },
13718       { "viewport.playfield",   &viewport.playfield[i]  },
13719       { "viewport.door_1",      &viewport.door_1[i]     },
13720       { "viewport.door_2",      &viewport.door_2[i]     }
13721     };
13722
13723     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13724     {
13725       struct TokenIntPtrInfo vp_config[] =
13726       {
13727         { ".x",                 &vp_struct[j].struct_ptr->x             },
13728         { ".y",                 &vp_struct[j].struct_ptr->y             },
13729         { ".width",             &vp_struct[j].struct_ptr->width         },
13730         { ".height",            &vp_struct[j].struct_ptr->height        },
13731         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13732         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13733         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13734         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13735         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13736         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13737         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13738         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13739         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13740         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13741         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13742         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13743         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13744         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13745         { ".align",             &vp_struct[j].struct_ptr->align         },
13746         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13747       };
13748
13749       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13750       {
13751         char *token = getStringCat2(vp_struct[j].token_prefix,
13752                                     vp_config[k].token);
13753         char *value = getHashEntry(setup_file_hash, token);
13754
13755         if (value != NULL)
13756           *vp_config[k].value = get_token_parameter_value(token, value);
13757
13758         free(token);
13759       }
13760     }
13761   }
13762
13763   // special case: initialize with default values that may be overwritten
13764   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13765   for (i = 0; title_info[i].info != NULL; i++)
13766   {
13767     struct TitleFadingInfo *info = title_info[i].info;
13768     char *base_token = title_info[i].text;
13769
13770     for (j = 0; title_tokens[j].type != -1; j++)
13771     {
13772       char *token = getStringCat2(base_token, title_tokens[j].text);
13773       char *value = getHashEntry(setup_file_hash, token);
13774
13775       if (value != NULL)
13776       {
13777         int parameter_value = get_token_parameter_value(token, value);
13778
13779         tfi = *info;
13780
13781         *(int *)title_tokens[j].value = (int)parameter_value;
13782
13783         *info = tfi;
13784       }
13785
13786       free(token);
13787     }
13788   }
13789
13790   // special case: initialize with default values that may be overwritten
13791   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13792   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13793   {
13794     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13795     char *base_token = titlemessage_arrays[i].text;
13796
13797     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13798     {
13799       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13800       char *value = getHashEntry(setup_file_hash, token);
13801
13802       if (value != NULL)
13803       {
13804         int parameter_value = get_token_parameter_value(token, value);
13805
13806         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13807         {
13808           tmi = array[k];
13809
13810           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13811             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13812           else
13813             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13814
13815           array[k] = tmi;
13816         }
13817       }
13818
13819       free(token);
13820     }
13821   }
13822
13823   // read (and overwrite with) values that may be specified in config file
13824   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13825
13826   // special case: check if network and preview player positions are redefined
13827   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13828
13829   freeSetupFileHash(setup_file_hash);
13830 }
13831
13832 void LoadMenuDesignSettings(void)
13833 {
13834   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13835
13836   InitMenuDesignSettings_Static();
13837   InitMenuDesignSettings_SpecialPreProcessing();
13838   InitMenuDesignSettings_PreviewPlayers();
13839
13840   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13841   {
13842     // first look for special settings configured in level series config
13843     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13844
13845     if (fileExists(filename_base))
13846       LoadMenuDesignSettingsFromFilename(filename_base);
13847   }
13848
13849   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13850
13851   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13852     LoadMenuDesignSettingsFromFilename(filename_local);
13853
13854   InitMenuDesignSettings_SpecialPostProcessing();
13855 }
13856
13857 void LoadMenuDesignSettings_AfterGraphics(void)
13858 {
13859   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13860 }
13861
13862 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13863                                 boolean ignore_defaults)
13864 {
13865   int i;
13866
13867   for (i = 0; sound_config_vars[i].token != NULL; i++)
13868   {
13869     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13870
13871     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13872     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13873       continue;
13874
13875     if (value != NULL)
13876       *sound_config_vars[i].value =
13877         get_token_parameter_value(sound_config_vars[i].token, value);
13878   }
13879 }
13880
13881 void InitSoundSettings_Static(void)
13882 {
13883   // always start with reliable default values from static default config
13884   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13885 }
13886
13887 static void LoadSoundSettingsFromFilename(char *filename)
13888 {
13889   SetupFileHash *setup_file_hash;
13890
13891   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13892     return;
13893
13894   // read (and overwrite with) values that may be specified in config file
13895   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13896
13897   freeSetupFileHash(setup_file_hash);
13898 }
13899
13900 void LoadSoundSettings(void)
13901 {
13902   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13903
13904   InitSoundSettings_Static();
13905
13906   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13907   {
13908     // first look for special settings configured in level series config
13909     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13910
13911     if (fileExists(filename_base))
13912       LoadSoundSettingsFromFilename(filename_base);
13913   }
13914
13915   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13916
13917   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13918     LoadSoundSettingsFromFilename(filename_local);
13919 }
13920
13921 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13922 {
13923   char *filename = getEditorSetupFilename();
13924   SetupFileList *setup_file_list, *list;
13925   SetupFileHash *element_hash;
13926   int num_unknown_tokens = 0;
13927   int i;
13928
13929   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13930     return;
13931
13932   element_hash = newSetupFileHash();
13933
13934   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13935     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13936
13937   // determined size may be larger than needed (due to unknown elements)
13938   *num_elements = 0;
13939   for (list = setup_file_list; list != NULL; list = list->next)
13940     (*num_elements)++;
13941
13942   // add space for up to 3 more elements for padding that may be needed
13943   *num_elements += 3;
13944
13945   // free memory for old list of elements, if needed
13946   checked_free(*elements);
13947
13948   // allocate memory for new list of elements
13949   *elements = checked_malloc(*num_elements * sizeof(int));
13950
13951   *num_elements = 0;
13952   for (list = setup_file_list; list != NULL; list = list->next)
13953   {
13954     char *value = getHashEntry(element_hash, list->token);
13955
13956     if (value == NULL)          // try to find obsolete token mapping
13957     {
13958       char *mapped_token = get_mapped_token(list->token);
13959
13960       if (mapped_token != NULL)
13961       {
13962         value = getHashEntry(element_hash, mapped_token);
13963
13964         free(mapped_token);
13965       }
13966     }
13967
13968     if (value != NULL)
13969     {
13970       (*elements)[(*num_elements)++] = atoi(value);
13971     }
13972     else
13973     {
13974       if (num_unknown_tokens == 0)
13975       {
13976         Warn("---");
13977         Warn("unknown token(s) found in config file:");
13978         Warn("- config file: '%s'", filename);
13979
13980         num_unknown_tokens++;
13981       }
13982
13983       Warn("- token: '%s'", list->token);
13984     }
13985   }
13986
13987   if (num_unknown_tokens > 0)
13988     Warn("---");
13989
13990   while (*num_elements % 4)     // pad with empty elements, if needed
13991     (*elements)[(*num_elements)++] = EL_EMPTY;
13992
13993   freeSetupFileList(setup_file_list);
13994   freeSetupFileHash(element_hash);
13995
13996 #if 0
13997   for (i = 0; i < *num_elements; i++)
13998     Debug("editor", "element '%s' [%d]\n",
13999           element_info[(*elements)[i]].token_name, (*elements)[i]);
14000 #endif
14001 }
14002
14003 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14004                                                      boolean is_sound)
14005 {
14006   SetupFileHash *setup_file_hash = NULL;
14007   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14008   char *filename_music, *filename_prefix, *filename_info;
14009   struct
14010   {
14011     char *token;
14012     char **value_ptr;
14013   }
14014   token_to_value_ptr[] =
14015   {
14016     { "title_header",   &tmp_music_file_info.title_header       },
14017     { "artist_header",  &tmp_music_file_info.artist_header      },
14018     { "album_header",   &tmp_music_file_info.album_header       },
14019     { "year_header",    &tmp_music_file_info.year_header        },
14020     { "played_header",  &tmp_music_file_info.played_header      },
14021
14022     { "title",          &tmp_music_file_info.title              },
14023     { "artist",         &tmp_music_file_info.artist             },
14024     { "album",          &tmp_music_file_info.album              },
14025     { "year",           &tmp_music_file_info.year               },
14026     { "played",         &tmp_music_file_info.played             },
14027
14028     { NULL,             NULL                                    },
14029   };
14030   int i;
14031
14032   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14033                     getCustomMusicFilename(basename));
14034
14035   if (filename_music == NULL)
14036     return NULL;
14037
14038   // ---------- try to replace file extension ----------
14039
14040   filename_prefix = getStringCopy(filename_music);
14041   if (strrchr(filename_prefix, '.') != NULL)
14042     *strrchr(filename_prefix, '.') = '\0';
14043   filename_info = getStringCat2(filename_prefix, ".txt");
14044
14045   if (fileExists(filename_info))
14046     setup_file_hash = loadSetupFileHash(filename_info);
14047
14048   free(filename_prefix);
14049   free(filename_info);
14050
14051   if (setup_file_hash == NULL)
14052   {
14053     // ---------- try to add file extension ----------
14054
14055     filename_prefix = getStringCopy(filename_music);
14056     filename_info = getStringCat2(filename_prefix, ".txt");
14057
14058     if (fileExists(filename_info))
14059       setup_file_hash = loadSetupFileHash(filename_info);
14060
14061     free(filename_prefix);
14062     free(filename_info);
14063   }
14064
14065   if (setup_file_hash == NULL)
14066     return NULL;
14067
14068   // ---------- music file info found ----------
14069
14070   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14071
14072   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14073   {
14074     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14075
14076     *token_to_value_ptr[i].value_ptr =
14077       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14078   }
14079
14080   tmp_music_file_info.basename = getStringCopy(basename);
14081   tmp_music_file_info.music = music;
14082   tmp_music_file_info.is_sound = is_sound;
14083
14084   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14085   *new_music_file_info = tmp_music_file_info;
14086
14087   return new_music_file_info;
14088 }
14089
14090 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14091 {
14092   return get_music_file_info_ext(basename, music, FALSE);
14093 }
14094
14095 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14096 {
14097   return get_music_file_info_ext(basename, sound, TRUE);
14098 }
14099
14100 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14101                                      char *basename, boolean is_sound)
14102 {
14103   for (; list != NULL; list = list->next)
14104     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14105       return TRUE;
14106
14107   return FALSE;
14108 }
14109
14110 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14111 {
14112   return music_info_listed_ext(list, basename, FALSE);
14113 }
14114
14115 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14116 {
14117   return music_info_listed_ext(list, basename, TRUE);
14118 }
14119
14120 void LoadMusicInfo(void)
14121 {
14122   int num_music_noconf = getMusicListSize_NoConf();
14123   int num_music = getMusicListSize();
14124   int num_sounds = getSoundListSize();
14125   struct FileInfo *music, *sound;
14126   struct MusicFileInfo *next, **new;
14127
14128   int i;
14129
14130   while (music_file_info != NULL)
14131   {
14132     next = music_file_info->next;
14133
14134     checked_free(music_file_info->basename);
14135
14136     checked_free(music_file_info->title_header);
14137     checked_free(music_file_info->artist_header);
14138     checked_free(music_file_info->album_header);
14139     checked_free(music_file_info->year_header);
14140     checked_free(music_file_info->played_header);
14141
14142     checked_free(music_file_info->title);
14143     checked_free(music_file_info->artist);
14144     checked_free(music_file_info->album);
14145     checked_free(music_file_info->year);
14146     checked_free(music_file_info->played);
14147
14148     free(music_file_info);
14149
14150     music_file_info = next;
14151   }
14152
14153   new = &music_file_info;
14154
14155   // get (configured or unconfigured) music file info for all levels
14156   for (i = leveldir_current->first_level;
14157        i <= leveldir_current->last_level; i++)
14158   {
14159     int music_nr;
14160
14161     if (levelset.music[i] != MUS_UNDEFINED)
14162     {
14163       // get music file info for configured level music
14164       music_nr = levelset.music[i];
14165     }
14166     else if (num_music_noconf > 0)
14167     {
14168       // get music file info for unconfigured level music
14169       int level_pos = i - leveldir_current->first_level;
14170
14171       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14172     }
14173     else
14174     {
14175       continue;
14176     }
14177
14178     char *basename = getMusicInfoEntryFilename(music_nr);
14179
14180     if (basename == NULL)
14181       continue;
14182
14183     if (!music_info_listed(music_file_info, basename))
14184     {
14185       *new = get_music_file_info(basename, music_nr);
14186
14187       if (*new != NULL)
14188         new = &(*new)->next;
14189     }
14190   }
14191
14192   // get music file info for all remaining configured music files
14193   for (i = 0; i < num_music; i++)
14194   {
14195     music = getMusicListEntry(i);
14196
14197     if (music->filename == NULL)
14198       continue;
14199
14200     if (strEqual(music->filename, UNDEFINED_FILENAME))
14201       continue;
14202
14203     // a configured file may be not recognized as music
14204     if (!FileIsMusic(music->filename))
14205       continue;
14206
14207     if (!music_info_listed(music_file_info, music->filename))
14208     {
14209       *new = get_music_file_info(music->filename, i);
14210
14211       if (*new != NULL)
14212         new = &(*new)->next;
14213     }
14214   }
14215
14216   // get sound file info for all configured sound files
14217   for (i = 0; i < num_sounds; i++)
14218   {
14219     sound = getSoundListEntry(i);
14220
14221     if (sound->filename == NULL)
14222       continue;
14223
14224     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14225       continue;
14226
14227     // a configured file may be not recognized as sound
14228     if (!FileIsSound(sound->filename))
14229       continue;
14230
14231     if (!sound_info_listed(music_file_info, sound->filename))
14232     {
14233       *new = get_sound_file_info(sound->filename, i);
14234       if (*new != NULL)
14235         new = &(*new)->next;
14236     }
14237   }
14238
14239   // add pointers to previous list nodes
14240
14241   struct MusicFileInfo *node = music_file_info;
14242
14243   while (node != NULL)
14244   {
14245     if (node->next)
14246       node->next->prev = node;
14247
14248     node = node->next;
14249   }
14250 }
14251
14252 static void add_helpanim_entry(int element, int action, int direction,
14253                                int delay, int *num_list_entries)
14254 {
14255   struct HelpAnimInfo *new_list_entry;
14256   (*num_list_entries)++;
14257
14258   helpanim_info =
14259     checked_realloc(helpanim_info,
14260                     *num_list_entries * sizeof(struct HelpAnimInfo));
14261   new_list_entry = &helpanim_info[*num_list_entries - 1];
14262
14263   new_list_entry->element = element;
14264   new_list_entry->action = action;
14265   new_list_entry->direction = direction;
14266   new_list_entry->delay = delay;
14267 }
14268
14269 static void print_unknown_token(char *filename, char *token, int token_nr)
14270 {
14271   if (token_nr == 0)
14272   {
14273     Warn("---");
14274     Warn("unknown token(s) found in config file:");
14275     Warn("- config file: '%s'", filename);
14276   }
14277
14278   Warn("- token: '%s'", token);
14279 }
14280
14281 static void print_unknown_token_end(int token_nr)
14282 {
14283   if (token_nr > 0)
14284     Warn("---");
14285 }
14286
14287 void LoadHelpAnimInfo(void)
14288 {
14289   char *filename = getHelpAnimFilename();
14290   SetupFileList *setup_file_list = NULL, *list;
14291   SetupFileHash *element_hash, *action_hash, *direction_hash;
14292   int num_list_entries = 0;
14293   int num_unknown_tokens = 0;
14294   int i;
14295
14296   if (fileExists(filename))
14297     setup_file_list = loadSetupFileList(filename);
14298
14299   if (setup_file_list == NULL)
14300   {
14301     // use reliable default values from static configuration
14302     SetupFileList *insert_ptr;
14303
14304     insert_ptr = setup_file_list =
14305       newSetupFileList(helpanim_config[0].token,
14306                        helpanim_config[0].value);
14307
14308     for (i = 1; helpanim_config[i].token; i++)
14309       insert_ptr = addListEntry(insert_ptr,
14310                                 helpanim_config[i].token,
14311                                 helpanim_config[i].value);
14312   }
14313
14314   element_hash   = newSetupFileHash();
14315   action_hash    = newSetupFileHash();
14316   direction_hash = newSetupFileHash();
14317
14318   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14319     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14320
14321   for (i = 0; i < NUM_ACTIONS; i++)
14322     setHashEntry(action_hash, element_action_info[i].suffix,
14323                  i_to_a(element_action_info[i].value));
14324
14325   // do not store direction index (bit) here, but direction value!
14326   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14327     setHashEntry(direction_hash, element_direction_info[i].suffix,
14328                  i_to_a(1 << element_direction_info[i].value));
14329
14330   for (list = setup_file_list; list != NULL; list = list->next)
14331   {
14332     char *element_token, *action_token, *direction_token;
14333     char *element_value, *action_value, *direction_value;
14334     int delay = atoi(list->value);
14335
14336     if (strEqual(list->token, "end"))
14337     {
14338       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14339
14340       continue;
14341     }
14342
14343     /* first try to break element into element/action/direction parts;
14344        if this does not work, also accept combined "element[.act][.dir]"
14345        elements (like "dynamite.active"), which are unique elements */
14346
14347     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14348     {
14349       element_value = getHashEntry(element_hash, list->token);
14350       if (element_value != NULL)        // element found
14351         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14352                            &num_list_entries);
14353       else
14354       {
14355         // no further suffixes found -- this is not an element
14356         print_unknown_token(filename, list->token, num_unknown_tokens++);
14357       }
14358
14359       continue;
14360     }
14361
14362     // token has format "<prefix>.<something>"
14363
14364     action_token = strchr(list->token, '.');    // suffix may be action ...
14365     direction_token = action_token;             // ... or direction
14366
14367     element_token = getStringCopy(list->token);
14368     *strchr(element_token, '.') = '\0';
14369
14370     element_value = getHashEntry(element_hash, element_token);
14371
14372     if (element_value == NULL)          // this is no element
14373     {
14374       element_value = getHashEntry(element_hash, list->token);
14375       if (element_value != NULL)        // combined element found
14376         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14377                            &num_list_entries);
14378       else
14379         print_unknown_token(filename, list->token, num_unknown_tokens++);
14380
14381       free(element_token);
14382
14383       continue;
14384     }
14385
14386     action_value = getHashEntry(action_hash, action_token);
14387
14388     if (action_value != NULL)           // action found
14389     {
14390       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14391                     &num_list_entries);
14392
14393       free(element_token);
14394
14395       continue;
14396     }
14397
14398     direction_value = getHashEntry(direction_hash, direction_token);
14399
14400     if (direction_value != NULL)        // direction found
14401     {
14402       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14403                          &num_list_entries);
14404
14405       free(element_token);
14406
14407       continue;
14408     }
14409
14410     if (strchr(action_token + 1, '.') == NULL)
14411     {
14412       // no further suffixes found -- this is not an action nor direction
14413
14414       element_value = getHashEntry(element_hash, list->token);
14415       if (element_value != NULL)        // combined element found
14416         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14417                            &num_list_entries);
14418       else
14419         print_unknown_token(filename, list->token, num_unknown_tokens++);
14420
14421       free(element_token);
14422
14423       continue;
14424     }
14425
14426     // token has format "<prefix>.<suffix>.<something>"
14427
14428     direction_token = strchr(action_token + 1, '.');
14429
14430     action_token = getStringCopy(action_token);
14431     *strchr(action_token + 1, '.') = '\0';
14432
14433     action_value = getHashEntry(action_hash, action_token);
14434
14435     if (action_value == NULL)           // this is no action
14436     {
14437       element_value = getHashEntry(element_hash, list->token);
14438       if (element_value != NULL)        // combined element found
14439         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14440                            &num_list_entries);
14441       else
14442         print_unknown_token(filename, list->token, num_unknown_tokens++);
14443
14444       free(element_token);
14445       free(action_token);
14446
14447       continue;
14448     }
14449
14450     direction_value = getHashEntry(direction_hash, direction_token);
14451
14452     if (direction_value != NULL)        // direction found
14453     {
14454       add_helpanim_entry(atoi(element_value), atoi(action_value),
14455                          atoi(direction_value), delay, &num_list_entries);
14456
14457       free(element_token);
14458       free(action_token);
14459
14460       continue;
14461     }
14462
14463     // this is no direction
14464
14465     element_value = getHashEntry(element_hash, list->token);
14466     if (element_value != NULL)          // combined element found
14467       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14468                          &num_list_entries);
14469     else
14470       print_unknown_token(filename, list->token, num_unknown_tokens++);
14471
14472     free(element_token);
14473     free(action_token);
14474   }
14475
14476   print_unknown_token_end(num_unknown_tokens);
14477
14478   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14479   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14480
14481   freeSetupFileList(setup_file_list);
14482   freeSetupFileHash(element_hash);
14483   freeSetupFileHash(action_hash);
14484   freeSetupFileHash(direction_hash);
14485
14486 #if 0
14487   for (i = 0; i < num_list_entries; i++)
14488     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14489           EL_NAME(helpanim_info[i].element),
14490           helpanim_info[i].element,
14491           helpanim_info[i].action,
14492           helpanim_info[i].direction,
14493           helpanim_info[i].delay);
14494 #endif
14495 }
14496
14497 void LoadHelpTextInfo(void)
14498 {
14499   char *filename = getHelpTextFilename();
14500   int i;
14501
14502   if (helptext_info != NULL)
14503   {
14504     freeSetupFileHash(helptext_info);
14505     helptext_info = NULL;
14506   }
14507
14508   if (fileExists(filename))
14509     helptext_info = loadSetupFileHash(filename);
14510
14511   if (helptext_info == NULL)
14512   {
14513     // use reliable default values from static configuration
14514     helptext_info = newSetupFileHash();
14515
14516     for (i = 0; helptext_config[i].token; i++)
14517       setHashEntry(helptext_info,
14518                    helptext_config[i].token,
14519                    helptext_config[i].value);
14520   }
14521
14522 #if 0
14523   BEGIN_HASH_ITERATION(helptext_info, itr)
14524   {
14525     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14526           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14527   }
14528   END_HASH_ITERATION(hash, itr)
14529 #endif
14530 }
14531
14532
14533 // ----------------------------------------------------------------------------
14534 // convert levels
14535 // ----------------------------------------------------------------------------
14536
14537 #define MAX_NUM_CONVERT_LEVELS          1000
14538
14539 void ConvertLevels(void)
14540 {
14541   static LevelDirTree *convert_leveldir = NULL;
14542   static int convert_level_nr = -1;
14543   static int num_levels_handled = 0;
14544   static int num_levels_converted = 0;
14545   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14546   int i;
14547
14548   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14549                                                global.convert_leveldir);
14550
14551   if (convert_leveldir == NULL)
14552     Fail("no such level identifier: '%s'", global.convert_leveldir);
14553
14554   leveldir_current = convert_leveldir;
14555
14556   if (global.convert_level_nr != -1)
14557   {
14558     convert_leveldir->first_level = global.convert_level_nr;
14559     convert_leveldir->last_level  = global.convert_level_nr;
14560   }
14561
14562   convert_level_nr = convert_leveldir->first_level;
14563
14564   PrintLine("=", 79);
14565   Print("Converting levels\n");
14566   PrintLine("-", 79);
14567   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14568   Print("Level series name:       '%s'\n", convert_leveldir->name);
14569   Print("Level series author:     '%s'\n", convert_leveldir->author);
14570   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14571   PrintLine("=", 79);
14572   Print("\n");
14573
14574   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14575     levels_failed[i] = FALSE;
14576
14577   while (convert_level_nr <= convert_leveldir->last_level)
14578   {
14579     char *level_filename;
14580     boolean new_level;
14581
14582     level_nr = convert_level_nr++;
14583
14584     Print("Level %03d: ", level_nr);
14585
14586     LoadLevel(level_nr);
14587     if (level.no_level_file || level.no_valid_file)
14588     {
14589       Print("(no level)\n");
14590       continue;
14591     }
14592
14593     Print("converting level ... ");
14594
14595 #if 0
14596     // special case: conversion of some EMC levels as requested by ACME
14597     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14598 #endif
14599
14600     level_filename = getDefaultLevelFilename(level_nr);
14601     new_level = !fileExists(level_filename);
14602
14603     if (new_level)
14604     {
14605       SaveLevel(level_nr);
14606
14607       num_levels_converted++;
14608
14609       Print("converted.\n");
14610     }
14611     else
14612     {
14613       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14614         levels_failed[level_nr] = TRUE;
14615
14616       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14617     }
14618
14619     num_levels_handled++;
14620   }
14621
14622   Print("\n");
14623   PrintLine("=", 79);
14624   Print("Number of levels handled: %d\n", num_levels_handled);
14625   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14626          (num_levels_handled ?
14627           num_levels_converted * 100 / num_levels_handled : 0));
14628   PrintLine("-", 79);
14629   Print("Summary (for automatic parsing by scripts):\n");
14630   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14631          convert_leveldir->identifier, num_levels_converted,
14632          num_levels_handled,
14633          (num_levels_handled ?
14634           num_levels_converted * 100 / num_levels_handled : 0));
14635
14636   if (num_levels_handled != num_levels_converted)
14637   {
14638     Print(", FAILED:");
14639     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14640       if (levels_failed[i])
14641         Print(" %03d", i);
14642   }
14643
14644   Print("\n");
14645   PrintLine("=", 79);
14646
14647   CloseAllAndExit(0);
14648 }
14649
14650
14651 // ----------------------------------------------------------------------------
14652 // create and save images for use in level sketches (raw BMP format)
14653 // ----------------------------------------------------------------------------
14654
14655 void CreateLevelSketchImages(void)
14656 {
14657   Bitmap *bitmap1;
14658   Bitmap *bitmap2;
14659   int i;
14660
14661   InitElementPropertiesGfxElement();
14662
14663   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14664   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14665
14666   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14667   {
14668     int element = getMappedElement(i);
14669     char basename1[16];
14670     char basename2[16];
14671     char *filename1;
14672     char *filename2;
14673
14674     sprintf(basename1, "%04d.bmp", i);
14675     sprintf(basename2, "%04ds.bmp", i);
14676
14677     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14678     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14679
14680     DrawSizedElement(0, 0, element, TILESIZE);
14681     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14682
14683     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14684       Fail("cannot save level sketch image file '%s'", filename1);
14685
14686     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14687     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14688
14689     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14690       Fail("cannot save level sketch image file '%s'", filename2);
14691
14692     free(filename1);
14693     free(filename2);
14694
14695     // create corresponding SQL statements (for normal and small images)
14696     if (i < 1000)
14697     {
14698       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14699       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14700     }
14701
14702     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14703     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14704
14705     // optional: create content for forum level sketch demonstration post
14706     if (options.debug)
14707       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14708   }
14709
14710   FreeBitmap(bitmap1);
14711   FreeBitmap(bitmap2);
14712
14713   if (options.debug)
14714     fprintf(stderr, "\n");
14715
14716   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14717
14718   CloseAllAndExit(0);
14719 }
14720
14721
14722 // ----------------------------------------------------------------------------
14723 // create and save images for element collecting animations (raw BMP format)
14724 // ----------------------------------------------------------------------------
14725
14726 static boolean createCollectImage(int element)
14727 {
14728   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14729 }
14730
14731 void CreateCollectElementImages(void)
14732 {
14733   int i, j;
14734   int num_steps = 8;
14735   int anim_frames = num_steps - 1;
14736   int tile_size = TILESIZE;
14737   int anim_width  = tile_size * anim_frames;
14738   int anim_height = tile_size;
14739   int num_collect_images = 0;
14740   int pos_collect_images = 0;
14741
14742   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14743     if (createCollectImage(i))
14744       num_collect_images++;
14745
14746   Info("Creating %d element collecting animation images ...",
14747        num_collect_images);
14748
14749   int dst_width  = anim_width * 2;
14750   int dst_height = anim_height * num_collect_images / 2;
14751   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14752   char *basename_bmp = "RocksCollect.bmp";
14753   char *basename_png = "RocksCollect.png";
14754   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14755   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14756   int len_filename_bmp = strlen(filename_bmp);
14757   int len_filename_png = strlen(filename_png);
14758   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14759   char cmd_convert[max_command_len];
14760
14761   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14762            filename_bmp,
14763            filename_png);
14764
14765   // force using RGBA surface for destination bitmap
14766   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14767                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14768
14769   dst_bitmap->surface =
14770     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14771
14772   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14773   {
14774     if (!createCollectImage(i))
14775       continue;
14776
14777     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14778     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14779     int graphic = el2img(i);
14780     char *token_name = element_info[i].token_name;
14781     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14782     Bitmap *src_bitmap;
14783     int src_x, src_y;
14784
14785     Info("- creating collecting image for '%s' ...", token_name);
14786
14787     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14788
14789     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14790                tile_size, tile_size, 0, 0);
14791
14792     // force using RGBA surface for temporary bitmap (using transparent black)
14793     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14794                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14795
14796     tmp_bitmap->surface =
14797       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14798
14799     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14800
14801     for (j = 0; j < anim_frames; j++)
14802     {
14803       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14804       int frame_size = frame_size_final * num_steps;
14805       int offset = (tile_size - frame_size_final) / 2;
14806       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14807
14808       while (frame_size > frame_size_final)
14809       {
14810         frame_size /= 2;
14811
14812         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14813
14814         FreeBitmap(frame_bitmap);
14815
14816         frame_bitmap = half_bitmap;
14817       }
14818
14819       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14820                        frame_size_final, frame_size_final,
14821                        dst_x + j * tile_size + offset, dst_y + offset);
14822
14823       FreeBitmap(frame_bitmap);
14824     }
14825
14826     tmp_bitmap->surface_masked = NULL;
14827
14828     FreeBitmap(tmp_bitmap);
14829
14830     pos_collect_images++;
14831   }
14832
14833   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14834     Fail("cannot save element collecting image file '%s'", filename_bmp);
14835
14836   FreeBitmap(dst_bitmap);
14837
14838   Info("Converting image file from BMP to PNG ...");
14839
14840   if (system(cmd_convert) != 0)
14841     Fail("converting image file failed");
14842
14843   unlink(filename_bmp);
14844
14845   Info("Done.");
14846
14847   CloseAllAndExit(0);
14848 }
14849
14850
14851 // ----------------------------------------------------------------------------
14852 // create and save images for custom and group elements (raw BMP format)
14853 // ----------------------------------------------------------------------------
14854
14855 void CreateCustomElementImages(char *directory)
14856 {
14857   char *src_basename = "RocksCE-template.ilbm";
14858   char *dst_basename = "RocksCE.bmp";
14859   char *src_filename = getPath2(directory, src_basename);
14860   char *dst_filename = getPath2(directory, dst_basename);
14861   Bitmap *src_bitmap;
14862   Bitmap *bitmap;
14863   int yoffset_ce = 0;
14864   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14865   int i;
14866
14867   InitVideoDefaults();
14868
14869   ReCreateBitmap(&backbuffer, video.width, video.height);
14870
14871   src_bitmap = LoadImage(src_filename);
14872
14873   bitmap = CreateBitmap(TILEX * 16 * 2,
14874                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14875                         DEFAULT_DEPTH);
14876
14877   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14878   {
14879     int x = i % 16;
14880     int y = i / 16;
14881     int ii = i + 1;
14882     int j;
14883
14884     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14885                TILEX * x, TILEY * y + yoffset_ce);
14886
14887     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14888                TILEX, TILEY,
14889                TILEX * x + TILEX * 16,
14890                TILEY * y + yoffset_ce);
14891
14892     for (j = 2; j >= 0; j--)
14893     {
14894       int c = ii % 10;
14895
14896       BlitBitmap(src_bitmap, bitmap,
14897                  TILEX + c * 7, 0, 6, 10,
14898                  TILEX * x + 6 + j * 7,
14899                  TILEY * y + 11 + yoffset_ce);
14900
14901       BlitBitmap(src_bitmap, bitmap,
14902                  TILEX + c * 8, TILEY, 6, 10,
14903                  TILEX * 16 + TILEX * x + 6 + j * 8,
14904                  TILEY * y + 10 + yoffset_ce);
14905
14906       ii /= 10;
14907     }
14908   }
14909
14910   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14911   {
14912     int x = i % 16;
14913     int y = i / 16;
14914     int ii = i + 1;
14915     int j;
14916
14917     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14918                TILEX * x, TILEY * y + yoffset_ge);
14919
14920     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14921                TILEX, TILEY,
14922                TILEX * x + TILEX * 16,
14923                TILEY * y + yoffset_ge);
14924
14925     for (j = 1; j >= 0; j--)
14926     {
14927       int c = ii % 10;
14928
14929       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14930                  TILEX * x + 6 + j * 10,
14931                  TILEY * y + 11 + yoffset_ge);
14932
14933       BlitBitmap(src_bitmap, bitmap,
14934                  TILEX + c * 8, TILEY + 12, 6, 10,
14935                  TILEX * 16 + TILEX * x + 10 + j * 8,
14936                  TILEY * y + 10 + yoffset_ge);
14937
14938       ii /= 10;
14939     }
14940   }
14941
14942   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14943     Fail("cannot save CE graphics file '%s'", dst_filename);
14944
14945   FreeBitmap(bitmap);
14946
14947   CloseAllAndExit(0);
14948 }