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