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