extended functions to map between R'n'D and BD cave and game elements
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658
659   {
660     EL_BD_CLOCK,                        -1,
661     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
662     &li.bd_clock_extra_time,            30
663   },
664
665   {
666     EL_BD_VOODOO_DOLL,                  -1,
667     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
668     &li.bd_voodoo_collects_diamonds,    FALSE
669   },
670   {
671     EL_BD_VOODOO_DOLL,                  -1,
672     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
673     &li.bd_voodoo_hurt_kills_player,    FALSE
674   },
675   {
676     EL_BD_VOODOO_DOLL,                  -1,
677     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
678     &li.bd_voodoo_dies_by_rock,         FALSE
679   },
680   {
681     EL_BD_VOODOO_DOLL,                  -1,
682     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
683     &li.bd_voodoo_vanish_by_explosion,  TRUE
684   },
685   {
686     EL_BD_VOODOO_DOLL,                  -1,
687     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
688     &li.bd_voodoo_penalty_time,         30
689   },
690
691   {
692     EL_BD_SLIME,                        -1,
693     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
694     &li.bd_slime_is_predictable,        TRUE
695   },
696   {
697     EL_BD_SLIME,                        -1,
698     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
699     &li.bd_slime_permeability_rate,     100
700   },
701   {
702     EL_BD_SLIME,                        -1,
703     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
704     &li.bd_slime_permeability_bits_c64, 0
705   },
706   {
707     EL_BD_SLIME,                        -1,
708     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
709     &li.bd_slime_random_seed_c64,       -1
710   },
711
712   {
713     EL_BD_ACID,                         -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_acid_eats_element,           EL_BD_SAND
716   },
717   {
718     EL_BD_ACID,                         -1,
719     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
720     &li.bd_acid_spread_rate,            3
721   },
722   {
723     EL_BD_ACID,                         -1,
724     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
725     &li.bd_acid_turns_to_element,       EL_EMPTY
726   },
727
728   {
729     EL_BD_BITER,                        -1,
730     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
731     &li.bd_biter_move_delay,            0
732   },
733   {
734     EL_BD_BITER,                        -1,
735     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
736     &li.bd_biter_eats_element,          EL_BD_DIAMOND
737   },
738
739   {
740     EL_BD_BLADDER,                      -1,
741     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
742     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
743   },
744
745   {
746     EL_BD_EXPANDABLE_WALL_ANY,          -1,
747     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
748     &li.bd_change_expanding_wall,       FALSE
749   },
750
751   {
752     EL_BD_REPLICATOR,                   -1,
753     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
754     &li.bd_replicators_active,          TRUE
755   },
756   {
757     EL_BD_REPLICATOR,                   -1,
758     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
759     &li.bd_replicator_create_delay,     4
760   },
761
762   {
763     EL_BD_CONVEYOR_LEFT,                -1,
764     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
765     &li.bd_conveyor_belts_active,       TRUE
766   },
767   {
768     EL_BD_CONVEYOR_LEFT,                -1,
769     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
770     &li.bd_conveyor_belts_changed,      FALSE
771   },
772
773   {
774     EL_BD_WATER,                        -1,
775     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
776     &li.bd_water_cannot_flow_down,      FALSE
777   },
778
779   // (the following values are related to various game elements)
780
781   {
782     EL_EMERALD,                         -1,
783     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
784     &li.score[SC_EMERALD],              10
785   },
786
787   {
788     EL_DIAMOND,                         -1,
789     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
790     &li.score[SC_DIAMOND],              10
791   },
792
793   {
794     EL_BUG,                             -1,
795     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
796     &li.score[SC_BUG],                  10
797   },
798
799   {
800     EL_SPACESHIP,                       -1,
801     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
802     &li.score[SC_SPACESHIP],            10
803   },
804
805   {
806     EL_PACMAN,                          -1,
807     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
808     &li.score[SC_PACMAN],               10
809   },
810
811   {
812     EL_NUT,                             -1,
813     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
814     &li.score[SC_NUT],                  10
815   },
816
817   {
818     EL_DYNAMITE,                        -1,
819     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
820     &li.score[SC_DYNAMITE],             10
821   },
822
823   {
824     EL_KEY_1,                           -1,
825     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
826     &li.score[SC_KEY],                  10
827   },
828
829   {
830     EL_PEARL,                           -1,
831     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
832     &li.score[SC_PEARL],                10
833   },
834
835   {
836     EL_CRYSTAL,                         -1,
837     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
838     &li.score[SC_CRYSTAL],              10
839   },
840
841   // (amoeba values used by R'n'D game engine only)
842   {
843     EL_BD_AMOEBA,                       -1,
844     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
845     &li.amoeba_content,                 EL_DIAMOND
846   },
847   {
848     EL_BD_AMOEBA,                       -1,
849     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
850     &li.amoeba_speed,                   10
851   },
852   {
853     EL_BD_AMOEBA,                       -1,
854     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
855     &li.grow_into_diggable,             TRUE
856   },
857   // (amoeba values used by BD game engine only)
858   {
859     EL_BD_AMOEBA,                       -1,
860     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
861     &li.bd_amoeba_wait_for_hatching,    FALSE
862   },
863   {
864     EL_BD_AMOEBA,                       -1,
865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
866     &li.bd_amoeba_start_immediately,    TRUE
867   },
868   {
869     EL_BD_AMOEBA,                       -1,
870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
871     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
872   },
873   {
874     EL_BD_AMOEBA,                       -1,
875     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
876     &li.bd_amoeba_threshold_too_big,    200
877   },
878   {
879     EL_BD_AMOEBA,                       -1,
880     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
881     &li.bd_amoeba_slow_growth_time,     200
882   },
883   {
884     EL_BD_AMOEBA,                       -1,
885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
886     &li.bd_amoeba_slow_growth_rate,     3
887   },
888   {
889     EL_BD_AMOEBA,                       -1,
890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
891     &li.bd_amoeba_fast_growth_rate,     25
892   },
893   {
894     EL_BD_AMOEBA,                       -1,
895     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
896     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
897   },
898   {
899     EL_BD_AMOEBA,                       -1,
900     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
901     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
902   },
903
904   {
905     EL_BD_AMOEBA_2,                     -1,
906     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
907     &li.bd_amoeba_2_threshold_too_big,  200
908   },
909   {
910     EL_BD_AMOEBA_2,                     -1,
911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
912     &li.bd_amoeba_2_slow_growth_time,   200
913   },
914   {
915     EL_BD_AMOEBA_2,                     -1,
916     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
917     &li.bd_amoeba_2_slow_growth_rate,   3
918   },
919   {
920     EL_BD_AMOEBA_2,                     -1,
921     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
922     &li.bd_amoeba_2_fast_growth_rate,   25
923   },
924   {
925     EL_BD_AMOEBA_2,                     -1,
926     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
927     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
928   },
929   {
930     EL_BD_AMOEBA_2,                     -1,
931     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
932     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
933   },
934   {
935     EL_BD_AMOEBA_2,                     -1,
936     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
937     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
938   },
939   {
940     EL_BD_AMOEBA_2,                     -1,
941     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
942     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
943   },
944
945   {
946     EL_YAMYAM,                          -1,
947     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
948     &li.yamyam_content,                 EL_ROCK, NULL,
949     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
950   },
951   {
952     EL_YAMYAM,                          -1,
953     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
954     &li.score[SC_YAMYAM],               10
955   },
956
957   {
958     EL_ROBOT,                           -1,
959     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
960     &li.score[SC_ROBOT],                10
961   },
962   {
963     EL_ROBOT,                           -1,
964     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
965     &li.slurp_score,                    10
966   },
967
968   {
969     EL_ROBOT_WHEEL,                     -1,
970     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
971     &li.time_wheel,                     10
972   },
973
974   {
975     EL_MAGIC_WALL,                      -1,
976     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
977     &li.time_magic_wall,                10
978   },
979
980   {
981     EL_GAME_OF_LIFE,                    -1,
982     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
983     &li.game_of_life[0],                2
984   },
985   {
986     EL_GAME_OF_LIFE,                    -1,
987     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
988     &li.game_of_life[1],                3
989   },
990   {
991     EL_GAME_OF_LIFE,                    -1,
992     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
993     &li.game_of_life[2],                3
994   },
995   {
996     EL_GAME_OF_LIFE,                    -1,
997     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
998     &li.game_of_life[3],                3
999   },
1000   {
1001     EL_GAME_OF_LIFE,                    -1,
1002     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1003     &li.use_life_bugs,                  FALSE
1004   },
1005
1006   {
1007     EL_BIOMAZE,                         -1,
1008     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1009     &li.biomaze[0],                     2
1010   },
1011   {
1012     EL_BIOMAZE,                         -1,
1013     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1014     &li.biomaze[1],                     3
1015   },
1016   {
1017     EL_BIOMAZE,                         -1,
1018     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1019     &li.biomaze[2],                     3
1020   },
1021   {
1022     EL_BIOMAZE,                         -1,
1023     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1024     &li.biomaze[3],                     3
1025   },
1026
1027   {
1028     EL_TIMEGATE_SWITCH,                 -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.time_timegate,                  10
1031   },
1032
1033   {
1034     EL_LIGHT_SWITCH_ACTIVE,             -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.time_light,                     10
1037   },
1038
1039   {
1040     EL_SHIELD_NORMAL,                   -1,
1041     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1042     &li.shield_normal_time,             10
1043   },
1044   {
1045     EL_SHIELD_NORMAL,                   -1,
1046     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1047     &li.score[SC_SHIELD],               10
1048   },
1049
1050   {
1051     EL_SHIELD_DEADLY,                   -1,
1052     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1053     &li.shield_deadly_time,             10
1054   },
1055   {
1056     EL_SHIELD_DEADLY,                   -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1058     &li.score[SC_SHIELD],               10
1059   },
1060
1061   {
1062     EL_EXTRA_TIME,                      -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.extra_time,                     10
1065   },
1066   {
1067     EL_EXTRA_TIME,                      -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1069     &li.extra_time_score,               10
1070   },
1071
1072   {
1073     EL_TIME_ORB_FULL,                   -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.time_orb_time,                  10
1076   },
1077   {
1078     EL_TIME_ORB_FULL,                   -1,
1079     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1080     &li.use_time_orb_bug,               FALSE
1081   },
1082
1083   {
1084     EL_SPRING,                          -1,
1085     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1086     &li.use_spring_bug,                 FALSE
1087   },
1088
1089   {
1090     EL_EMC_ANDROID,                     -1,
1091     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1092     &li.android_move_time,              10
1093   },
1094   {
1095     EL_EMC_ANDROID,                     -1,
1096     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1097     &li.android_clone_time,             10
1098   },
1099   {
1100     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1101     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1102     &li.android_clone_element[0],       EL_EMPTY, NULL,
1103     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1104   },
1105   {
1106     EL_EMC_ANDROID,                     -1,
1107     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1108     &li.android_clone_element[0],       EL_EMPTY, NULL,
1109     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1110   },
1111
1112   {
1113     EL_EMC_LENSES,                      -1,
1114     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1115     &li.lenses_score,                   10
1116   },
1117   {
1118     EL_EMC_LENSES,                      -1,
1119     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1120     &li.lenses_time,                    10
1121   },
1122
1123   {
1124     EL_EMC_MAGNIFIER,                   -1,
1125     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1126     &li.magnify_score,                  10
1127   },
1128   {
1129     EL_EMC_MAGNIFIER,                   -1,
1130     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1131     &li.magnify_time,                   10
1132   },
1133
1134   {
1135     EL_EMC_MAGIC_BALL,                  -1,
1136     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1137     &li.ball_time,                      10
1138   },
1139   {
1140     EL_EMC_MAGIC_BALL,                  -1,
1141     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1142     &li.ball_random,                    FALSE
1143   },
1144   {
1145     EL_EMC_MAGIC_BALL,                  -1,
1146     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1147     &li.ball_active_initial,            FALSE
1148   },
1149   {
1150     EL_EMC_MAGIC_BALL,                  -1,
1151     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1152     &li.ball_content,                   EL_EMPTY, NULL,
1153     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1154   },
1155
1156   {
1157     EL_SOKOBAN_FIELD_EMPTY,             -1,
1158     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1159     &li.sb_fields_needed,               TRUE
1160   },
1161
1162   {
1163     EL_SOKOBAN_OBJECT,                  -1,
1164     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1165     &li.sb_objects_needed,              TRUE
1166   },
1167
1168   {
1169     EL_MM_MCDUFFIN,                     -1,
1170     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1171     &li.mm_laser_red,                   FALSE
1172   },
1173   {
1174     EL_MM_MCDUFFIN,                     -1,
1175     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1176     &li.mm_laser_green,                 FALSE
1177   },
1178   {
1179     EL_MM_MCDUFFIN,                     -1,
1180     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1181     &li.mm_laser_blue,                  TRUE
1182   },
1183
1184   {
1185     EL_DF_LASER,                        -1,
1186     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1187     &li.df_laser_red,                   TRUE
1188   },
1189   {
1190     EL_DF_LASER,                        -1,
1191     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1192     &li.df_laser_green,                 TRUE
1193   },
1194   {
1195     EL_DF_LASER,                        -1,
1196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1197     &li.df_laser_blue,                  FALSE
1198   },
1199
1200   {
1201     EL_MM_FUSE_ACTIVE,                  -1,
1202     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1203     &li.mm_time_fuse,                   25
1204   },
1205   {
1206     EL_MM_BOMB,                         -1,
1207     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1208     &li.mm_time_bomb,                   75
1209   },
1210
1211   {
1212     EL_MM_GRAY_BALL,                    -1,
1213     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1214     &li.mm_time_ball,                   75
1215   },
1216   {
1217     EL_MM_GRAY_BALL,                    -1,
1218     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1219     &li.mm_ball_choice_mode,            ANIM_RANDOM
1220   },
1221   {
1222     EL_MM_GRAY_BALL,                    -1,
1223     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1224     &li.mm_ball_content,                EL_EMPTY, NULL,
1225     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1226   },
1227   {
1228     EL_MM_GRAY_BALL,                    -1,
1229     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1230     &li.rotate_mm_ball_content,         TRUE
1231   },
1232   {
1233     EL_MM_GRAY_BALL,                    -1,
1234     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1235     &li.explode_mm_ball,                FALSE
1236   },
1237
1238   {
1239     EL_MM_STEEL_BLOCK,                  -1,
1240     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1241     &li.mm_time_block,                  75
1242   },
1243   {
1244     EL_MM_LIGHTBALL,                    -1,
1245     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1246     &li.score[SC_ELEM_BONUS],           10
1247   },
1248
1249   {
1250     -1,                                 -1,
1251     -1,                                 -1,
1252     NULL,                               -1
1253   }
1254 };
1255
1256 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1257 {
1258   {
1259     -1,                                 -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1262   },
1263   {
1264     -1,                                 -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1267   },
1268
1269   {
1270     -1,                                 -1,
1271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1272     &xx_envelope.autowrap,              FALSE
1273   },
1274   {
1275     -1,                                 -1,
1276     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1277     &xx_envelope.centered,              FALSE
1278   },
1279
1280   {
1281     -1,                                 -1,
1282     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1283     &xx_envelope.text,                  -1, NULL,
1284     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1285     &xx_default_string_empty[0]
1286   },
1287
1288   {
1289     -1,                                 -1,
1290     -1,                                 -1,
1291     NULL,                               -1
1292   }
1293 };
1294
1295 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1296 {
1297   {
1298     -1,                                 -1,
1299     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1300     &xx_ei.description[0],              -1,
1301     &yy_ei.description[0],
1302     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1303     &xx_default_description[0]
1304   },
1305
1306   {
1307     -1,                                 -1,
1308     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1309     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1310     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1311   },
1312 #if ENABLE_RESERVED_CODE
1313   // (reserved for later use)
1314   {
1315     -1,                                 -1,
1316     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1317     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1318     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1319   },
1320 #endif
1321
1322   {
1323     -1,                                 -1,
1324     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1325     &xx_ei.use_gfx_element,             FALSE,
1326     &yy_ei.use_gfx_element
1327   },
1328   {
1329     -1,                                 -1,
1330     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1331     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1332     &yy_ei.gfx_element_initial
1333   },
1334
1335   {
1336     -1,                                 -1,
1337     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1338     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1339     &yy_ei.access_direction
1340   },
1341
1342   {
1343     -1,                                 -1,
1344     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1345     &xx_ei.collect_score_initial,       10,
1346     &yy_ei.collect_score_initial
1347   },
1348   {
1349     -1,                                 -1,
1350     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1351     &xx_ei.collect_count_initial,       1,
1352     &yy_ei.collect_count_initial
1353   },
1354
1355   {
1356     -1,                                 -1,
1357     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1358     &xx_ei.ce_value_fixed_initial,      0,
1359     &yy_ei.ce_value_fixed_initial
1360   },
1361   {
1362     -1,                                 -1,
1363     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1364     &xx_ei.ce_value_random_initial,     0,
1365     &yy_ei.ce_value_random_initial
1366   },
1367   {
1368     -1,                                 -1,
1369     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1370     &xx_ei.use_last_ce_value,           FALSE,
1371     &yy_ei.use_last_ce_value
1372   },
1373
1374   {
1375     -1,                                 -1,
1376     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1377     &xx_ei.push_delay_fixed,            8,
1378     &yy_ei.push_delay_fixed
1379   },
1380   {
1381     -1,                                 -1,
1382     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1383     &xx_ei.push_delay_random,           8,
1384     &yy_ei.push_delay_random
1385   },
1386   {
1387     -1,                                 -1,
1388     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1389     &xx_ei.drop_delay_fixed,            0,
1390     &yy_ei.drop_delay_fixed
1391   },
1392   {
1393     -1,                                 -1,
1394     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1395     &xx_ei.drop_delay_random,           0,
1396     &yy_ei.drop_delay_random
1397   },
1398   {
1399     -1,                                 -1,
1400     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1401     &xx_ei.move_delay_fixed,            0,
1402     &yy_ei.move_delay_fixed
1403   },
1404   {
1405     -1,                                 -1,
1406     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1407     &xx_ei.move_delay_random,           0,
1408     &yy_ei.move_delay_random
1409   },
1410   {
1411     -1,                                 -1,
1412     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1413     &xx_ei.step_delay_fixed,            0,
1414     &yy_ei.step_delay_fixed
1415   },
1416   {
1417     -1,                                 -1,
1418     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1419     &xx_ei.step_delay_random,           0,
1420     &yy_ei.step_delay_random
1421   },
1422
1423   {
1424     -1,                                 -1,
1425     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1426     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1427     &yy_ei.move_pattern
1428   },
1429   {
1430     -1,                                 -1,
1431     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1432     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1433     &yy_ei.move_direction_initial
1434   },
1435   {
1436     -1,                                 -1,
1437     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1438     &xx_ei.move_stepsize,               TILEX / 8,
1439     &yy_ei.move_stepsize
1440   },
1441
1442   {
1443     -1,                                 -1,
1444     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1445     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1446     &yy_ei.move_enter_element
1447   },
1448   {
1449     -1,                                 -1,
1450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1451     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1452     &yy_ei.move_leave_element
1453   },
1454   {
1455     -1,                                 -1,
1456     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1457     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1458     &yy_ei.move_leave_type
1459   },
1460
1461   {
1462     -1,                                 -1,
1463     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1464     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1465     &yy_ei.slippery_type
1466   },
1467
1468   {
1469     -1,                                 -1,
1470     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1471     &xx_ei.explosion_type,              EXPLODES_3X3,
1472     &yy_ei.explosion_type
1473   },
1474   {
1475     -1,                                 -1,
1476     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1477     &xx_ei.explosion_delay,             16,
1478     &yy_ei.explosion_delay
1479   },
1480   {
1481     -1,                                 -1,
1482     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1483     &xx_ei.ignition_delay,              8,
1484     &yy_ei.ignition_delay
1485   },
1486
1487   {
1488     -1,                                 -1,
1489     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1490     &xx_ei.content,                     EL_EMPTY_SPACE,
1491     &yy_ei.content,
1492     &xx_num_contents,                   1, 1
1493   },
1494
1495   // ---------- "num_change_pages" must be the last entry ---------------------
1496
1497   {
1498     -1,                                 SAVE_CONF_ALWAYS,
1499     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1500     &xx_ei.num_change_pages,            1,
1501     &yy_ei.num_change_pages
1502   },
1503
1504   {
1505     -1,                                 -1,
1506     -1,                                 -1,
1507     NULL,                               -1,
1508     NULL
1509   }
1510 };
1511
1512 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1513 {
1514   // ---------- "current_change_page" must be the first entry -----------------
1515
1516   {
1517     -1,                                 SAVE_CONF_ALWAYS,
1518     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1519     &xx_current_change_page,            -1
1520   },
1521
1522   // ---------- (the remaining entries can be in any order) -------------------
1523
1524   {
1525     -1,                                 -1,
1526     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1527     &xx_change.can_change,              FALSE
1528   },
1529
1530   {
1531     -1,                                 -1,
1532     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1533     &xx_event_bits[0],                  0
1534   },
1535   {
1536     -1,                                 -1,
1537     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1538     &xx_event_bits[1],                  0
1539   },
1540
1541   {
1542     -1,                                 -1,
1543     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1544     &xx_change.trigger_player,          CH_PLAYER_ANY
1545   },
1546   {
1547     -1,                                 -1,
1548     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1549     &xx_change.trigger_side,            CH_SIDE_ANY
1550   },
1551   {
1552     -1,                                 -1,
1553     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1554     &xx_change.trigger_page,            CH_PAGE_ANY
1555   },
1556
1557   {
1558     -1,                                 -1,
1559     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1560     &xx_change.target_element,          EL_EMPTY_SPACE
1561   },
1562
1563   {
1564     -1,                                 -1,
1565     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1566     &xx_change.delay_fixed,             0
1567   },
1568   {
1569     -1,                                 -1,
1570     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1571     &xx_change.delay_random,            0
1572   },
1573   {
1574     -1,                                 -1,
1575     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1576     &xx_change.delay_frames,            FRAMES_PER_SECOND
1577   },
1578
1579   {
1580     -1,                                 -1,
1581     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1582     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1583   },
1584
1585   {
1586     -1,                                 -1,
1587     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1588     &xx_change.explode,                 FALSE
1589   },
1590   {
1591     -1,                                 -1,
1592     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1593     &xx_change.use_target_content,      FALSE
1594   },
1595   {
1596     -1,                                 -1,
1597     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1598     &xx_change.only_if_complete,        FALSE
1599   },
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1603     &xx_change.use_random_replace,      FALSE
1604   },
1605   {
1606     -1,                                 -1,
1607     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1608     &xx_change.random_percentage,       100
1609   },
1610   {
1611     -1,                                 -1,
1612     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1613     &xx_change.replace_when,            CP_WHEN_EMPTY
1614   },
1615
1616   {
1617     -1,                                 -1,
1618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1619     &xx_change.has_action,              FALSE
1620   },
1621   {
1622     -1,                                 -1,
1623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1624     &xx_change.action_type,             CA_NO_ACTION
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1629     &xx_change.action_mode,             CA_MODE_UNDEFINED
1630   },
1631   {
1632     -1,                                 -1,
1633     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1634     &xx_change.action_arg,              CA_ARG_UNDEFINED
1635   },
1636
1637   {
1638     -1,                                 -1,
1639     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1640     &xx_change.action_element,          EL_EMPTY_SPACE
1641   },
1642
1643   {
1644     -1,                                 -1,
1645     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1646     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1647     &xx_num_contents,                   1, 1
1648   },
1649
1650   {
1651     -1,                                 -1,
1652     -1,                                 -1,
1653     NULL,                               -1
1654   }
1655 };
1656
1657 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1658 {
1659   {
1660     -1,                                 -1,
1661     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1662     &xx_ei.description[0],              -1, NULL,
1663     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1664     &xx_default_description[0]
1665   },
1666
1667   {
1668     -1,                                 -1,
1669     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1670     &xx_ei.use_gfx_element,             FALSE
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1675     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1676   },
1677
1678   {
1679     -1,                                 -1,
1680     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1681     &xx_group.choice_mode,              ANIM_RANDOM
1682   },
1683
1684   {
1685     -1,                                 -1,
1686     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1687     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1688     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1689   },
1690
1691   {
1692     -1,                                 -1,
1693     -1,                                 -1,
1694     NULL,                               -1
1695   }
1696 };
1697
1698 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1699 {
1700   {
1701     -1,                                 -1,
1702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1703     &xx_ei.use_gfx_element,             FALSE
1704   },
1705   {
1706     -1,                                 -1,
1707     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1708     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1709   },
1710
1711   {
1712     -1,                                 -1,
1713     -1,                                 -1,
1714     NULL,                               -1
1715   }
1716 };
1717
1718 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1719 {
1720   {
1721     EL_PLAYER_1,                        -1,
1722     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1723     &li.block_snap_field,               TRUE
1724   },
1725   {
1726     EL_PLAYER_1,                        -1,
1727     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1728     &li.continuous_snapping,            TRUE
1729   },
1730   {
1731     EL_PLAYER_1,                        -1,
1732     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1733     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1734   },
1735   {
1736     EL_PLAYER_1,                        -1,
1737     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1738     &li.use_start_element[0],           FALSE
1739   },
1740   {
1741     EL_PLAYER_1,                        -1,
1742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1743     &li.start_element[0],               EL_PLAYER_1
1744   },
1745   {
1746     EL_PLAYER_1,                        -1,
1747     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1748     &li.use_artwork_element[0],         FALSE
1749   },
1750   {
1751     EL_PLAYER_1,                        -1,
1752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1753     &li.artwork_element[0],             EL_PLAYER_1
1754   },
1755   {
1756     EL_PLAYER_1,                        -1,
1757     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1758     &li.use_explosion_element[0],       FALSE
1759   },
1760   {
1761     EL_PLAYER_1,                        -1,
1762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1763     &li.explosion_element[0],           EL_PLAYER_1
1764   },
1765
1766   {
1767     -1,                                 -1,
1768     -1,                                 -1,
1769     NULL,                               -1
1770   }
1771 };
1772
1773 static struct
1774 {
1775   int filetype;
1776   char *id;
1777 }
1778 filetype_id_list[] =
1779 {
1780   { LEVEL_FILE_TYPE_RND,        "RND"   },
1781   { LEVEL_FILE_TYPE_BD,         "BD"    },
1782   { LEVEL_FILE_TYPE_EM,         "EM"    },
1783   { LEVEL_FILE_TYPE_SP,         "SP"    },
1784   { LEVEL_FILE_TYPE_DX,         "DX"    },
1785   { LEVEL_FILE_TYPE_SB,         "SB"    },
1786   { LEVEL_FILE_TYPE_DC,         "DC"    },
1787   { LEVEL_FILE_TYPE_MM,         "MM"    },
1788   { LEVEL_FILE_TYPE_MM,         "DF"    },
1789   { -1,                         NULL    },
1790 };
1791
1792
1793 // ============================================================================
1794 // level file functions
1795 // ============================================================================
1796
1797 static boolean check_special_flags(char *flag)
1798 {
1799   if (strEqual(options.special_flags, flag) ||
1800       strEqual(leveldir_current->special_flags, flag))
1801     return TRUE;
1802
1803   return FALSE;
1804 }
1805
1806 static struct DateInfo getCurrentDate(void)
1807 {
1808   time_t epoch_seconds = time(NULL);
1809   struct tm *now = localtime(&epoch_seconds);
1810   struct DateInfo date;
1811
1812   date.year  = now->tm_year + 1900;
1813   date.month = now->tm_mon  + 1;
1814   date.day   = now->tm_mday;
1815
1816   date.src   = DATE_SRC_CLOCK;
1817
1818   return date;
1819 }
1820
1821 static void resetEventFlags(struct ElementChangeInfo *change)
1822 {
1823   int i;
1824
1825   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1826     change->has_event[i] = FALSE;
1827 }
1828
1829 static void resetEventBits(void)
1830 {
1831   int i;
1832
1833   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1834     xx_event_bits[i] = 0;
1835 }
1836
1837 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1838 {
1839   int i;
1840
1841   /* important: only change event flag if corresponding event bit is set
1842      (this is because all xx_event_bits[] values are loaded separately,
1843      and all xx_event_bits[] values are set back to zero before loading
1844      another value xx_event_bits[x] (each value representing 32 flags)) */
1845
1846   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1847     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1848       change->has_event[i] = TRUE;
1849 }
1850
1851 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1852 {
1853   int i;
1854
1855   /* in contrast to the above function setEventFlagsFromEventBits(), it
1856      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1857      depending on the corresponding change->has_event[i] values here, as
1858      all xx_event_bits[] values are reset in resetEventBits() before */
1859
1860   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1861     if (change->has_event[i])
1862       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1863 }
1864
1865 static char *getDefaultElementDescription(struct ElementInfo *ei)
1866 {
1867   static char description[MAX_ELEMENT_NAME_LEN + 1];
1868   char *default_description = (ei->custom_description != NULL ?
1869                                ei->custom_description :
1870                                ei->editor_description);
1871   int i;
1872
1873   // always start with reliable default values
1874   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1875     description[i] = '\0';
1876
1877   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1878   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1879
1880   return &description[0];
1881 }
1882
1883 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1884 {
1885   char *default_description = getDefaultElementDescription(ei);
1886   int i;
1887
1888   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1889     ei->description[i] = default_description[i];
1890 }
1891
1892 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1893 {
1894   int i;
1895
1896   for (i = 0; conf[i].data_type != -1; i++)
1897   {
1898     int default_value = conf[i].default_value;
1899     int data_type = conf[i].data_type;
1900     int conf_type = conf[i].conf_type;
1901     int byte_mask = conf_type & CONF_MASK_BYTES;
1902
1903     if (byte_mask == CONF_MASK_MULTI_BYTES)
1904     {
1905       int default_num_entities = conf[i].default_num_entities;
1906       int max_num_entities = conf[i].max_num_entities;
1907
1908       *(int *)(conf[i].num_entities) = default_num_entities;
1909
1910       if (data_type == TYPE_STRING)
1911       {
1912         char *default_string = conf[i].default_string;
1913         char *string = (char *)(conf[i].value);
1914
1915         strncpy(string, default_string, max_num_entities);
1916       }
1917       else if (data_type == TYPE_ELEMENT_LIST)
1918       {
1919         int *element_array = (int *)(conf[i].value);
1920         int j;
1921
1922         for (j = 0; j < max_num_entities; j++)
1923           element_array[j] = default_value;
1924       }
1925       else if (data_type == TYPE_CONTENT_LIST)
1926       {
1927         struct Content *content = (struct Content *)(conf[i].value);
1928         int c, x, y;
1929
1930         for (c = 0; c < max_num_entities; c++)
1931           for (y = 0; y < 3; y++)
1932             for (x = 0; x < 3; x++)
1933               content[c].e[x][y] = default_value;
1934       }
1935     }
1936     else        // constant size configuration data (1, 2 or 4 bytes)
1937     {
1938       if (data_type == TYPE_BOOLEAN)
1939         *(boolean *)(conf[i].value) = default_value;
1940       else
1941         *(int *)    (conf[i].value) = default_value;
1942     }
1943   }
1944 }
1945
1946 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1947 {
1948   int i;
1949
1950   for (i = 0; conf[i].data_type != -1; i++)
1951   {
1952     int data_type = conf[i].data_type;
1953     int conf_type = conf[i].conf_type;
1954     int byte_mask = conf_type & CONF_MASK_BYTES;
1955
1956     if (byte_mask == CONF_MASK_MULTI_BYTES)
1957     {
1958       int max_num_entities = conf[i].max_num_entities;
1959
1960       if (data_type == TYPE_STRING)
1961       {
1962         char *string      = (char *)(conf[i].value);
1963         char *string_copy = (char *)(conf[i].value_copy);
1964
1965         strncpy(string_copy, string, max_num_entities);
1966       }
1967       else if (data_type == TYPE_ELEMENT_LIST)
1968       {
1969         int *element_array      = (int *)(conf[i].value);
1970         int *element_array_copy = (int *)(conf[i].value_copy);
1971         int j;
1972
1973         for (j = 0; j < max_num_entities; j++)
1974           element_array_copy[j] = element_array[j];
1975       }
1976       else if (data_type == TYPE_CONTENT_LIST)
1977       {
1978         struct Content *content      = (struct Content *)(conf[i].value);
1979         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1980         int c, x, y;
1981
1982         for (c = 0; c < max_num_entities; c++)
1983           for (y = 0; y < 3; y++)
1984             for (x = 0; x < 3; x++)
1985               content_copy[c].e[x][y] = content[c].e[x][y];
1986       }
1987     }
1988     else        // constant size configuration data (1, 2 or 4 bytes)
1989     {
1990       if (data_type == TYPE_BOOLEAN)
1991         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1992       else
1993         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1994     }
1995   }
1996 }
1997
1998 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1999 {
2000   int i;
2001
2002   xx_ei = *ei_from;     // copy element data into temporary buffer
2003   yy_ei = *ei_to;       // copy element data into temporary buffer
2004
2005   copyConfigFromConfigList(chunk_config_CUSX_base);
2006
2007   *ei_from = xx_ei;
2008   *ei_to   = yy_ei;
2009
2010   // ---------- reinitialize and copy change pages ----------
2011
2012   ei_to->num_change_pages = ei_from->num_change_pages;
2013   ei_to->current_change_page = ei_from->current_change_page;
2014
2015   setElementChangePages(ei_to, ei_to->num_change_pages);
2016
2017   for (i = 0; i < ei_to->num_change_pages; i++)
2018     ei_to->change_page[i] = ei_from->change_page[i];
2019
2020   // ---------- copy group element info ----------
2021   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2022     *ei_to->group = *ei_from->group;
2023
2024   // mark this custom element as modified
2025   ei_to->modified_settings = TRUE;
2026 }
2027
2028 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2029 {
2030   int change_page_size = sizeof(struct ElementChangeInfo);
2031
2032   ei->num_change_pages = MAX(1, change_pages);
2033
2034   ei->change_page =
2035     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2036
2037   if (ei->current_change_page >= ei->num_change_pages)
2038     ei->current_change_page = ei->num_change_pages - 1;
2039
2040   ei->change = &ei->change_page[ei->current_change_page];
2041 }
2042
2043 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2044 {
2045   xx_change = *change;          // copy change data into temporary buffer
2046
2047   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2048
2049   *change = xx_change;
2050
2051   resetEventFlags(change);
2052
2053   change->direct_action = 0;
2054   change->other_action = 0;
2055
2056   change->pre_change_function = NULL;
2057   change->change_function = NULL;
2058   change->post_change_function = NULL;
2059 }
2060
2061 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2062 {
2063   int i, x, y;
2064
2065   li = *level;          // copy level data into temporary buffer
2066   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2067   *level = li;          // copy temporary buffer back to level data
2068
2069   setLevelInfoToDefaults_BD();
2070   setLevelInfoToDefaults_EM();
2071   setLevelInfoToDefaults_SP();
2072   setLevelInfoToDefaults_MM();
2073
2074   level->native_bd_level = &native_bd_level;
2075   level->native_em_level = &native_em_level;
2076   level->native_sp_level = &native_sp_level;
2077   level->native_mm_level = &native_mm_level;
2078
2079   level->file_version = FILE_VERSION_ACTUAL;
2080   level->game_version = GAME_VERSION_ACTUAL;
2081
2082   level->creation_date = getCurrentDate();
2083
2084   level->encoding_16bit_field  = TRUE;
2085   level->encoding_16bit_yamyam = TRUE;
2086   level->encoding_16bit_amoeba = TRUE;
2087
2088   // clear level name and level author string buffers
2089   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2090     level->name[i] = '\0';
2091   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2092     level->author[i] = '\0';
2093
2094   // set level name and level author to default values
2095   strcpy(level->name, NAMELESS_LEVEL_NAME);
2096   strcpy(level->author, ANONYMOUS_NAME);
2097
2098   // set level playfield to playable default level with player and exit
2099   for (x = 0; x < MAX_LEV_FIELDX; x++)
2100     for (y = 0; y < MAX_LEV_FIELDY; y++)
2101       level->field[x][y] = EL_SAND;
2102
2103   level->field[0][0] = EL_PLAYER_1;
2104   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2105
2106   BorderElement = EL_STEELWALL;
2107
2108   // detect custom elements when loading them
2109   level->file_has_custom_elements = FALSE;
2110
2111   // set all bug compatibility flags to "false" => do not emulate this bug
2112   level->use_action_after_change_bug = FALSE;
2113
2114   if (leveldir_current)
2115   {
2116     // try to determine better author name than 'anonymous'
2117     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2118     {
2119       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2120       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2121     }
2122     else
2123     {
2124       switch (LEVELCLASS(leveldir_current))
2125       {
2126         case LEVELCLASS_TUTORIAL:
2127           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2128           break;
2129
2130         case LEVELCLASS_CONTRIB:
2131           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2132           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2133           break;
2134
2135         case LEVELCLASS_PRIVATE:
2136           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2137           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2138           break;
2139
2140         default:
2141           // keep default value
2142           break;
2143       }
2144     }
2145   }
2146 }
2147
2148 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2149 {
2150   static boolean clipboard_elements_initialized = FALSE;
2151   int i;
2152
2153   InitElementPropertiesStatic();
2154
2155   li = *level;          // copy level data into temporary buffer
2156   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2157   *level = li;          // copy temporary buffer back to level data
2158
2159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2160   {
2161     int element = i;
2162     struct ElementInfo *ei = &element_info[element];
2163
2164     if (element == EL_MM_GRAY_BALL)
2165     {
2166       struct LevelInfo_MM *level_mm = level->native_mm_level;
2167       int j;
2168
2169       for (j = 0; j < level->num_mm_ball_contents; j++)
2170         level->mm_ball_content[j] =
2171           map_element_MM_to_RND(level_mm->ball_content[j]);
2172     }
2173
2174     // never initialize clipboard elements after the very first time
2175     // (to be able to use clipboard elements between several levels)
2176     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2177       continue;
2178
2179     if (IS_ENVELOPE(element))
2180     {
2181       int envelope_nr = element - EL_ENVELOPE_1;
2182
2183       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2184
2185       level->envelope[envelope_nr] = xx_envelope;
2186     }
2187
2188     if (IS_CUSTOM_ELEMENT(element) ||
2189         IS_GROUP_ELEMENT(element) ||
2190         IS_INTERNAL_ELEMENT(element))
2191     {
2192       xx_ei = *ei;      // copy element data into temporary buffer
2193
2194       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2195
2196       *ei = xx_ei;
2197     }
2198
2199     setElementChangePages(ei, 1);
2200     setElementChangeInfoToDefaults(ei->change);
2201
2202     if (IS_CUSTOM_ELEMENT(element) ||
2203         IS_GROUP_ELEMENT(element))
2204     {
2205       setElementDescriptionToDefault(ei);
2206
2207       ei->modified_settings = FALSE;
2208     }
2209
2210     if (IS_CUSTOM_ELEMENT(element) ||
2211         IS_INTERNAL_ELEMENT(element))
2212     {
2213       // internal values used in level editor
2214
2215       ei->access_type = 0;
2216       ei->access_layer = 0;
2217       ei->access_protected = 0;
2218       ei->walk_to_action = 0;
2219       ei->smash_targets = 0;
2220       ei->deadliness = 0;
2221
2222       ei->can_explode_by_fire = FALSE;
2223       ei->can_explode_smashed = FALSE;
2224       ei->can_explode_impact = FALSE;
2225
2226       ei->current_change_page = 0;
2227     }
2228
2229     if (IS_GROUP_ELEMENT(element) ||
2230         IS_INTERNAL_ELEMENT(element))
2231     {
2232       struct ElementGroupInfo *group;
2233
2234       // initialize memory for list of elements in group
2235       if (ei->group == NULL)
2236         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2237
2238       group = ei->group;
2239
2240       xx_group = *group;        // copy group data into temporary buffer
2241
2242       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2243
2244       *group = xx_group;
2245     }
2246
2247     if (IS_EMPTY_ELEMENT(element) ||
2248         IS_INTERNAL_ELEMENT(element))
2249     {
2250       xx_ei = *ei;              // copy element data into temporary buffer
2251
2252       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2253
2254       *ei = xx_ei;
2255     }
2256   }
2257
2258   clipboard_elements_initialized = TRUE;
2259 }
2260
2261 static void setLevelInfoToDefaults(struct LevelInfo *level,
2262                                    boolean level_info_only,
2263                                    boolean reset_file_status)
2264 {
2265   setLevelInfoToDefaults_Level(level);
2266
2267   if (!level_info_only)
2268     setLevelInfoToDefaults_Elements(level);
2269
2270   if (reset_file_status)
2271   {
2272     level->no_valid_file = FALSE;
2273     level->no_level_file = FALSE;
2274   }
2275
2276   level->changed = FALSE;
2277 }
2278
2279 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2280 {
2281   level_file_info->nr = 0;
2282   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2283   level_file_info->packed = FALSE;
2284
2285   setString(&level_file_info->basename, NULL);
2286   setString(&level_file_info->filename, NULL);
2287 }
2288
2289 int getMappedElement_SB(int, boolean);
2290
2291 static void ActivateLevelTemplate(void)
2292 {
2293   int x, y;
2294
2295   if (check_special_flags("load_xsb_to_ces"))
2296   {
2297     // fill smaller playfields with padding "beyond border wall" elements
2298     if (level.fieldx < level_template.fieldx ||
2299         level.fieldy < level_template.fieldy)
2300     {
2301       short field[level.fieldx][level.fieldy];
2302       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2303       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2304       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2305       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2306
2307       // copy old playfield (which is smaller than the visible area)
2308       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2309         field[x][y] = level.field[x][y];
2310
2311       // fill new, larger playfield with "beyond border wall" elements
2312       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2313         level.field[x][y] = getMappedElement_SB('_', TRUE);
2314
2315       // copy the old playfield to the middle of the new playfield
2316       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2317         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2318
2319       level.fieldx = new_fieldx;
2320       level.fieldy = new_fieldy;
2321     }
2322   }
2323
2324   // Currently there is no special action needed to activate the template
2325   // data, because 'element_info' property settings overwrite the original
2326   // level data, while all other variables do not change.
2327
2328   // Exception: 'from_level_template' elements in the original level playfield
2329   // are overwritten with the corresponding elements at the same position in
2330   // playfield from the level template.
2331
2332   for (x = 0; x < level.fieldx; x++)
2333     for (y = 0; y < level.fieldy; y++)
2334       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2335         level.field[x][y] = level_template.field[x][y];
2336
2337   if (check_special_flags("load_xsb_to_ces"))
2338   {
2339     struct LevelInfo level_backup = level;
2340
2341     // overwrite all individual level settings from template level settings
2342     level = level_template;
2343
2344     // restore level file info
2345     level.file_info = level_backup.file_info;
2346
2347     // restore playfield size
2348     level.fieldx = level_backup.fieldx;
2349     level.fieldy = level_backup.fieldy;
2350
2351     // restore playfield content
2352     for (x = 0; x < level.fieldx; x++)
2353       for (y = 0; y < level.fieldy; y++)
2354         level.field[x][y] = level_backup.field[x][y];
2355
2356     // restore name and author from individual level
2357     strcpy(level.name,   level_backup.name);
2358     strcpy(level.author, level_backup.author);
2359
2360     // restore flag "use_custom_template"
2361     level.use_custom_template = level_backup.use_custom_template;
2362   }
2363 }
2364
2365 static boolean checkForPackageFromBasename_BD(char *basename)
2366 {
2367   // check for native BD level file extensions
2368   if (!strSuffixLower(basename, ".bd") &&
2369       !strSuffixLower(basename, ".bdr") &&
2370       !strSuffixLower(basename, ".brc") &&
2371       !strSuffixLower(basename, ".gds"))
2372     return FALSE;
2373
2374   // check for standard single-level BD files (like "001.bd")
2375   if (strSuffixLower(basename, ".bd") &&
2376       strlen(basename) == 6 &&
2377       basename[0] >= '0' && basename[0] <= '9' &&
2378       basename[1] >= '0' && basename[1] <= '9' &&
2379       basename[2] >= '0' && basename[2] <= '9')
2380     return FALSE;
2381
2382   // this is a level package in native BD file format
2383   return TRUE;
2384 }
2385
2386 static char *getLevelFilenameFromBasename(char *basename)
2387 {
2388   static char *filename = NULL;
2389
2390   checked_free(filename);
2391
2392   filename = getPath2(getCurrentLevelDir(), basename);
2393
2394   return filename;
2395 }
2396
2397 static int getFileTypeFromBasename(char *basename)
2398 {
2399   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2400
2401   static char *filename = NULL;
2402   struct stat file_status;
2403
2404   // ---------- try to determine file type from filename ----------
2405
2406   // check for typical filename of a Supaplex level package file
2407   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2408     return LEVEL_FILE_TYPE_SP;
2409
2410   // check for typical filename of a Diamond Caves II level package file
2411   if (strSuffixLower(basename, ".dc") ||
2412       strSuffixLower(basename, ".dc2"))
2413     return LEVEL_FILE_TYPE_DC;
2414
2415   // check for typical filename of a Sokoban level package file
2416   if (strSuffixLower(basename, ".xsb") &&
2417       strchr(basename, '%') == NULL)
2418     return LEVEL_FILE_TYPE_SB;
2419
2420   // check for typical filename of a Boulder Dash (GDash) level package file
2421   if (checkForPackageFromBasename_BD(basename))
2422     return LEVEL_FILE_TYPE_BD;
2423
2424   // ---------- try to determine file type from filesize ----------
2425
2426   checked_free(filename);
2427   filename = getPath2(getCurrentLevelDir(), basename);
2428
2429   if (stat(filename, &file_status) == 0)
2430   {
2431     // check for typical filesize of a Supaplex level package file
2432     if (file_status.st_size == 170496)
2433       return LEVEL_FILE_TYPE_SP;
2434   }
2435
2436   return LEVEL_FILE_TYPE_UNKNOWN;
2437 }
2438
2439 static int getFileTypeFromMagicBytes(char *filename, int type)
2440 {
2441   File *file;
2442
2443   if ((file = openFile(filename, MODE_READ)))
2444   {
2445     char chunk_name[CHUNK_ID_LEN + 1];
2446
2447     getFileChunkBE(file, chunk_name, NULL);
2448
2449     if (strEqual(chunk_name, "MMII") ||
2450         strEqual(chunk_name, "MIRR"))
2451       type = LEVEL_FILE_TYPE_MM;
2452
2453     closeFile(file);
2454   }
2455
2456   return type;
2457 }
2458
2459 static boolean checkForPackageFromBasename(char *basename)
2460 {
2461   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2462   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2463
2464   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2465 }
2466
2467 static char *getSingleLevelBasenameExt(int nr, char *extension)
2468 {
2469   static char basename[MAX_FILENAME_LEN];
2470
2471   if (nr < 0)
2472     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2473   else
2474     sprintf(basename, "%03d.%s", nr, extension);
2475
2476   return basename;
2477 }
2478
2479 static char *getSingleLevelBasename(int nr)
2480 {
2481   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2482 }
2483
2484 static char *getPackedLevelBasename(int type)
2485 {
2486   static char basename[MAX_FILENAME_LEN];
2487   char *directory = getCurrentLevelDir();
2488   Directory *dir;
2489   DirectoryEntry *dir_entry;
2490
2491   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2492
2493   if ((dir = openDirectory(directory)) == NULL)
2494   {
2495     Warn("cannot read current level directory '%s'", directory);
2496
2497     return basename;
2498   }
2499
2500   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2501   {
2502     char *entry_basename = dir_entry->basename;
2503     int entry_type = getFileTypeFromBasename(entry_basename);
2504
2505     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2506     {
2507       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2508           type == entry_type)
2509       {
2510         strcpy(basename, entry_basename);
2511
2512         break;
2513       }
2514     }
2515   }
2516
2517   closeDirectory(dir);
2518
2519   return basename;
2520 }
2521
2522 static char *getSingleLevelFilename(int nr)
2523 {
2524   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2525 }
2526
2527 #if ENABLE_UNUSED_CODE
2528 static char *getPackedLevelFilename(int type)
2529 {
2530   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2531 }
2532 #endif
2533
2534 char *getDefaultLevelFilename(int nr)
2535 {
2536   return getSingleLevelFilename(nr);
2537 }
2538
2539 #if ENABLE_UNUSED_CODE
2540 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2541                                                  int type)
2542 {
2543   lfi->type = type;
2544   lfi->packed = FALSE;
2545
2546   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2547   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2548 }
2549 #endif
2550
2551 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2552                                                  int type, char *format, ...)
2553 {
2554   static char basename[MAX_FILENAME_LEN];
2555   va_list ap;
2556
2557   va_start(ap, format);
2558   vsprintf(basename, format, ap);
2559   va_end(ap);
2560
2561   lfi->type = type;
2562   lfi->packed = FALSE;
2563
2564   setString(&lfi->basename, basename);
2565   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2566 }
2567
2568 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2569                                                  int type)
2570 {
2571   lfi->type = type;
2572   lfi->packed = TRUE;
2573
2574   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2575   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2576 }
2577
2578 static int getFiletypeFromID(char *filetype_id)
2579 {
2580   char *filetype_id_lower;
2581   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2582   int i;
2583
2584   if (filetype_id == NULL)
2585     return LEVEL_FILE_TYPE_UNKNOWN;
2586
2587   filetype_id_lower = getStringToLower(filetype_id);
2588
2589   for (i = 0; filetype_id_list[i].id != NULL; i++)
2590   {
2591     char *id_lower = getStringToLower(filetype_id_list[i].id);
2592     
2593     if (strEqual(filetype_id_lower, id_lower))
2594       filetype = filetype_id_list[i].filetype;
2595
2596     free(id_lower);
2597
2598     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2599       break;
2600   }
2601
2602   free(filetype_id_lower);
2603
2604   return filetype;
2605 }
2606
2607 char *getLocalLevelTemplateFilename(void)
2608 {
2609   return getDefaultLevelFilename(-1);
2610 }
2611
2612 char *getGlobalLevelTemplateFilename(void)
2613 {
2614   // global variable "leveldir_current" must be modified in the loop below
2615   LevelDirTree *leveldir_current_last = leveldir_current;
2616   char *filename = NULL;
2617
2618   // check for template level in path from current to topmost tree node
2619
2620   while (leveldir_current != NULL)
2621   {
2622     filename = getDefaultLevelFilename(-1);
2623
2624     if (fileExists(filename))
2625       break;
2626
2627     leveldir_current = leveldir_current->node_parent;
2628   }
2629
2630   // restore global variable "leveldir_current" modified in above loop
2631   leveldir_current = leveldir_current_last;
2632
2633   return filename;
2634 }
2635
2636 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2637 {
2638   int nr = lfi->nr;
2639
2640   // special case: level number is negative => check for level template file
2641   if (nr < 0)
2642   {
2643     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2644                                          getSingleLevelBasename(-1));
2645
2646     // replace local level template filename with global template filename
2647     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2648
2649     // no fallback if template file not existing
2650     return;
2651   }
2652
2653   // special case: check for file name/pattern specified in "levelinfo.conf"
2654   if (leveldir_current->level_filename != NULL)
2655   {
2656     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2657
2658     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2659                                          leveldir_current->level_filename, nr);
2660
2661     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2662
2663     if (fileExists(lfi->filename))
2664       return;
2665   }
2666   else if (leveldir_current->level_filetype != NULL)
2667   {
2668     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2669
2670     // check for specified native level file with standard file name
2671     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2672                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2673     if (fileExists(lfi->filename))
2674       return;
2675   }
2676
2677   // check for native Rocks'n'Diamonds level file
2678   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2679                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2680   if (fileExists(lfi->filename))
2681     return;
2682
2683   // check for native Boulder Dash level file
2684   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2685   if (fileExists(lfi->filename))
2686     return;
2687
2688   // check for Emerald Mine level file (V1)
2689   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2690                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2691   if (fileExists(lfi->filename))
2692     return;
2693   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2694                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2695   if (fileExists(lfi->filename))
2696     return;
2697
2698   // check for Emerald Mine level file (V2 to V5)
2699   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2700   if (fileExists(lfi->filename))
2701     return;
2702
2703   // check for Emerald Mine level file (V6 / single mode)
2704   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2705   if (fileExists(lfi->filename))
2706     return;
2707   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2708   if (fileExists(lfi->filename))
2709     return;
2710
2711   // check for Emerald Mine level file (V6 / teamwork mode)
2712   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2713   if (fileExists(lfi->filename))
2714     return;
2715   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2716   if (fileExists(lfi->filename))
2717     return;
2718
2719   // check for various packed level file formats
2720   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2721   if (fileExists(lfi->filename))
2722     return;
2723
2724   // no known level file found -- use default values (and fail later)
2725   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2726                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2727 }
2728
2729 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2730 {
2731   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2732     lfi->type = getFileTypeFromBasename(lfi->basename);
2733
2734   if (lfi->type == LEVEL_FILE_TYPE_RND)
2735     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2736 }
2737
2738 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2739 {
2740   // always start with reliable default values
2741   setFileInfoToDefaults(level_file_info);
2742
2743   level_file_info->nr = nr;     // set requested level number
2744
2745   determineLevelFileInfo_Filename(level_file_info);
2746   determineLevelFileInfo_Filetype(level_file_info);
2747 }
2748
2749 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2750                               struct LevelFileInfo *lfi_to)
2751 {
2752   lfi_to->nr     = lfi_from->nr;
2753   lfi_to->type   = lfi_from->type;
2754   lfi_to->packed = lfi_from->packed;
2755
2756   setString(&lfi_to->basename, lfi_from->basename);
2757   setString(&lfi_to->filename, lfi_from->filename);
2758 }
2759
2760 // ----------------------------------------------------------------------------
2761 // functions for loading R'n'D level
2762 // ----------------------------------------------------------------------------
2763
2764 int getMappedElement(int element)
2765 {
2766   // remap some (historic, now obsolete) elements
2767
2768   switch (element)
2769   {
2770     case EL_PLAYER_OBSOLETE:
2771       element = EL_PLAYER_1;
2772       break;
2773
2774     case EL_KEY_OBSOLETE:
2775       element = EL_KEY_1;
2776       break;
2777
2778     case EL_EM_KEY_1_FILE_OBSOLETE:
2779       element = EL_EM_KEY_1;
2780       break;
2781
2782     case EL_EM_KEY_2_FILE_OBSOLETE:
2783       element = EL_EM_KEY_2;
2784       break;
2785
2786     case EL_EM_KEY_3_FILE_OBSOLETE:
2787       element = EL_EM_KEY_3;
2788       break;
2789
2790     case EL_EM_KEY_4_FILE_OBSOLETE:
2791       element = EL_EM_KEY_4;
2792       break;
2793
2794     case EL_ENVELOPE_OBSOLETE:
2795       element = EL_ENVELOPE_1;
2796       break;
2797
2798     case EL_SP_EMPTY:
2799       element = EL_EMPTY;
2800       break;
2801
2802     default:
2803       if (element >= NUM_FILE_ELEMENTS)
2804       {
2805         Warn("invalid level element %d", element);
2806
2807         element = EL_UNKNOWN;
2808       }
2809       break;
2810   }
2811
2812   return element;
2813 }
2814
2815 static int getMappedElementByVersion(int element, int game_version)
2816 {
2817   // remap some elements due to certain game version
2818
2819   if (game_version <= VERSION_IDENT(2,2,0,0))
2820   {
2821     // map game font elements
2822     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2823                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2824                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2825                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2826   }
2827
2828   if (game_version < VERSION_IDENT(3,0,0,0))
2829   {
2830     // map Supaplex gravity tube elements
2831     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2832                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2833                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2834                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2835                element);
2836   }
2837
2838   return element;
2839 }
2840
2841 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2842 {
2843   level->file_version = getFileVersion(file);
2844   level->game_version = getFileVersion(file);
2845
2846   return chunk_size;
2847 }
2848
2849 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2850 {
2851   level->creation_date.year  = getFile16BitBE(file);
2852   level->creation_date.month = getFile8Bit(file);
2853   level->creation_date.day   = getFile8Bit(file);
2854
2855   level->creation_date.src   = DATE_SRC_LEVELFILE;
2856
2857   return chunk_size;
2858 }
2859
2860 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2861 {
2862   int initial_player_stepsize;
2863   int initial_player_gravity;
2864   int i, x, y;
2865
2866   level->fieldx = getFile8Bit(file);
2867   level->fieldy = getFile8Bit(file);
2868
2869   level->time           = getFile16BitBE(file);
2870   level->gems_needed    = getFile16BitBE(file);
2871
2872   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2873     level->name[i] = getFile8Bit(file);
2874   level->name[MAX_LEVEL_NAME_LEN] = 0;
2875
2876   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2877     level->score[i] = getFile8Bit(file);
2878
2879   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2880   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2881     for (y = 0; y < 3; y++)
2882       for (x = 0; x < 3; x++)
2883         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2884
2885   level->amoeba_speed           = getFile8Bit(file);
2886   level->time_magic_wall        = getFile8Bit(file);
2887   level->time_wheel             = getFile8Bit(file);
2888   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2889
2890   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2891                                    STEPSIZE_NORMAL);
2892
2893   for (i = 0; i < MAX_PLAYERS; i++)
2894     level->initial_player_stepsize[i] = initial_player_stepsize;
2895
2896   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2897
2898   for (i = 0; i < MAX_PLAYERS; i++)
2899     level->initial_player_gravity[i] = initial_player_gravity;
2900
2901   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2902   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2903
2904   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2905
2906   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2907   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2908   level->can_move_into_acid_bits = getFile32BitBE(file);
2909   level->dont_collide_with_bits = getFile8Bit(file);
2910
2911   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2912   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2913
2914   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2915   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2916   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2917
2918   level->game_engine_type       = getFile8Bit(file);
2919
2920   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2921
2922   return chunk_size;
2923 }
2924
2925 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2926 {
2927   int i;
2928
2929   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2930     level->name[i] = getFile8Bit(file);
2931   level->name[MAX_LEVEL_NAME_LEN] = 0;
2932
2933   return chunk_size;
2934 }
2935
2936 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2937 {
2938   int i;
2939
2940   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2941     level->author[i] = getFile8Bit(file);
2942   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2943
2944   return chunk_size;
2945 }
2946
2947 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2948 {
2949   int x, y;
2950   int chunk_size_expected = level->fieldx * level->fieldy;
2951
2952   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2953      stored with 16-bit encoding (and should be twice as big then).
2954      Even worse, playfield data was stored 16-bit when only yamyam content
2955      contained 16-bit elements and vice versa. */
2956
2957   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2958     chunk_size_expected *= 2;
2959
2960   if (chunk_size_expected != chunk_size)
2961   {
2962     ReadUnusedBytesFromFile(file, chunk_size);
2963     return chunk_size_expected;
2964   }
2965
2966   for (y = 0; y < level->fieldy; y++)
2967     for (x = 0; x < level->fieldx; x++)
2968       level->field[x][y] =
2969         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2970                          getFile8Bit(file));
2971   return chunk_size;
2972 }
2973
2974 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2975 {
2976   int i, x, y;
2977   int header_size = 4;
2978   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2979   int chunk_size_expected = header_size + content_size;
2980
2981   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2982      stored with 16-bit encoding (and should be twice as big then).
2983      Even worse, playfield data was stored 16-bit when only yamyam content
2984      contained 16-bit elements and vice versa. */
2985
2986   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2987     chunk_size_expected += content_size;
2988
2989   if (chunk_size_expected != chunk_size)
2990   {
2991     ReadUnusedBytesFromFile(file, chunk_size);
2992     return chunk_size_expected;
2993   }
2994
2995   getFile8Bit(file);
2996   level->num_yamyam_contents = getFile8Bit(file);
2997   getFile8Bit(file);
2998   getFile8Bit(file);
2999
3000   // correct invalid number of content fields -- should never happen
3001   if (level->num_yamyam_contents < 1 ||
3002       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3003     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3004
3005   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3006     for (y = 0; y < 3; y++)
3007       for (x = 0; x < 3; x++)
3008         level->yamyam_content[i].e[x][y] =
3009           getMappedElement(level->encoding_16bit_field ?
3010                            getFile16BitBE(file) : getFile8Bit(file));
3011   return chunk_size;
3012 }
3013
3014 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3015 {
3016   int i, x, y;
3017   int element;
3018   int num_contents;
3019   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3020
3021   element = getMappedElement(getFile16BitBE(file));
3022   num_contents = getFile8Bit(file);
3023
3024   getFile8Bit(file);    // content x size (unused)
3025   getFile8Bit(file);    // content y size (unused)
3026
3027   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3028
3029   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3030     for (y = 0; y < 3; y++)
3031       for (x = 0; x < 3; x++)
3032         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3033
3034   // correct invalid number of content fields -- should never happen
3035   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3036     num_contents = STD_ELEMENT_CONTENTS;
3037
3038   if (element == EL_YAMYAM)
3039   {
3040     level->num_yamyam_contents = num_contents;
3041
3042     for (i = 0; i < num_contents; i++)
3043       for (y = 0; y < 3; y++)
3044         for (x = 0; x < 3; x++)
3045           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3046   }
3047   else if (element == EL_BD_AMOEBA)
3048   {
3049     level->amoeba_content = content_array[0][0][0];
3050   }
3051   else
3052   {
3053     Warn("cannot load content for element '%d'", element);
3054   }
3055
3056   return chunk_size;
3057 }
3058
3059 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3060 {
3061   int i;
3062   int element;
3063   int envelope_nr;
3064   int envelope_len;
3065   int chunk_size_expected;
3066
3067   element = getMappedElement(getFile16BitBE(file));
3068   if (!IS_ENVELOPE(element))
3069     element = EL_ENVELOPE_1;
3070
3071   envelope_nr = element - EL_ENVELOPE_1;
3072
3073   envelope_len = getFile16BitBE(file);
3074
3075   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3076   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3077
3078   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3079
3080   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3081   if (chunk_size_expected != chunk_size)
3082   {
3083     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3084     return chunk_size_expected;
3085   }
3086
3087   for (i = 0; i < envelope_len; i++)
3088     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3089
3090   return chunk_size;
3091 }
3092
3093 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3094 {
3095   int num_changed_custom_elements = getFile16BitBE(file);
3096   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3097   int i;
3098
3099   if (chunk_size_expected != chunk_size)
3100   {
3101     ReadUnusedBytesFromFile(file, chunk_size - 2);
3102     return chunk_size_expected;
3103   }
3104
3105   for (i = 0; i < num_changed_custom_elements; i++)
3106   {
3107     int element = getMappedElement(getFile16BitBE(file));
3108     int properties = getFile32BitBE(file);
3109
3110     if (IS_CUSTOM_ELEMENT(element))
3111       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3112     else
3113       Warn("invalid custom element number %d", element);
3114
3115     // older game versions that wrote level files with CUS1 chunks used
3116     // different default push delay values (not yet stored in level file)
3117     element_info[element].push_delay_fixed = 2;
3118     element_info[element].push_delay_random = 8;
3119   }
3120
3121   level->file_has_custom_elements = TRUE;
3122
3123   return chunk_size;
3124 }
3125
3126 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3127 {
3128   int num_changed_custom_elements = getFile16BitBE(file);
3129   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3130   int i;
3131
3132   if (chunk_size_expected != chunk_size)
3133   {
3134     ReadUnusedBytesFromFile(file, chunk_size - 2);
3135     return chunk_size_expected;
3136   }
3137
3138   for (i = 0; i < num_changed_custom_elements; i++)
3139   {
3140     int element = getMappedElement(getFile16BitBE(file));
3141     int custom_target_element = getMappedElement(getFile16BitBE(file));
3142
3143     if (IS_CUSTOM_ELEMENT(element))
3144       element_info[element].change->target_element = custom_target_element;
3145     else
3146       Warn("invalid custom element number %d", element);
3147   }
3148
3149   level->file_has_custom_elements = TRUE;
3150
3151   return chunk_size;
3152 }
3153
3154 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3155 {
3156   int num_changed_custom_elements = getFile16BitBE(file);
3157   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3158   int i, j, x, y;
3159
3160   if (chunk_size_expected != chunk_size)
3161   {
3162     ReadUnusedBytesFromFile(file, chunk_size - 2);
3163     return chunk_size_expected;
3164   }
3165
3166   for (i = 0; i < num_changed_custom_elements; i++)
3167   {
3168     int element = getMappedElement(getFile16BitBE(file));
3169     struct ElementInfo *ei = &element_info[element];
3170     unsigned int event_bits;
3171
3172     if (!IS_CUSTOM_ELEMENT(element))
3173     {
3174       Warn("invalid custom element number %d", element);
3175
3176       element = EL_INTERNAL_DUMMY;
3177     }
3178
3179     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3180       ei->description[j] = getFile8Bit(file);
3181     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3182
3183     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3184
3185     // some free bytes for future properties and padding
3186     ReadUnusedBytesFromFile(file, 7);
3187
3188     ei->use_gfx_element = getFile8Bit(file);
3189     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3190
3191     ei->collect_score_initial = getFile8Bit(file);
3192     ei->collect_count_initial = getFile8Bit(file);
3193
3194     ei->push_delay_fixed = getFile16BitBE(file);
3195     ei->push_delay_random = getFile16BitBE(file);
3196     ei->move_delay_fixed = getFile16BitBE(file);
3197     ei->move_delay_random = getFile16BitBE(file);
3198
3199     ei->move_pattern = getFile16BitBE(file);
3200     ei->move_direction_initial = getFile8Bit(file);
3201     ei->move_stepsize = getFile8Bit(file);
3202
3203     for (y = 0; y < 3; y++)
3204       for (x = 0; x < 3; x++)
3205         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3206
3207     // bits 0 - 31 of "has_event[]"
3208     event_bits = getFile32BitBE(file);
3209     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3210       if (event_bits & (1u << j))
3211         ei->change->has_event[j] = TRUE;
3212
3213     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3214
3215     ei->change->delay_fixed = getFile16BitBE(file);
3216     ei->change->delay_random = getFile16BitBE(file);
3217     ei->change->delay_frames = getFile16BitBE(file);
3218
3219     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3220
3221     ei->change->explode = getFile8Bit(file);
3222     ei->change->use_target_content = getFile8Bit(file);
3223     ei->change->only_if_complete = getFile8Bit(file);
3224     ei->change->use_random_replace = getFile8Bit(file);
3225
3226     ei->change->random_percentage = getFile8Bit(file);
3227     ei->change->replace_when = getFile8Bit(file);
3228
3229     for (y = 0; y < 3; y++)
3230       for (x = 0; x < 3; x++)
3231         ei->change->target_content.e[x][y] =
3232           getMappedElement(getFile16BitBE(file));
3233
3234     ei->slippery_type = getFile8Bit(file);
3235
3236     // some free bytes for future properties and padding
3237     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3238
3239     // mark that this custom element has been modified
3240     ei->modified_settings = TRUE;
3241   }
3242
3243   level->file_has_custom_elements = TRUE;
3244
3245   return chunk_size;
3246 }
3247
3248 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3249 {
3250   struct ElementInfo *ei;
3251   int chunk_size_expected;
3252   int element;
3253   int i, j, x, y;
3254
3255   // ---------- custom element base property values (96 bytes) ----------------
3256
3257   element = getMappedElement(getFile16BitBE(file));
3258
3259   if (!IS_CUSTOM_ELEMENT(element))
3260   {
3261     Warn("invalid custom element number %d", element);
3262
3263     ReadUnusedBytesFromFile(file, chunk_size - 2);
3264
3265     return chunk_size;
3266   }
3267
3268   ei = &element_info[element];
3269
3270   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3271     ei->description[i] = getFile8Bit(file);
3272   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3273
3274   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3275
3276   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3277
3278   ei->num_change_pages = getFile8Bit(file);
3279
3280   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3281   if (chunk_size_expected != chunk_size)
3282   {
3283     ReadUnusedBytesFromFile(file, chunk_size - 43);
3284     return chunk_size_expected;
3285   }
3286
3287   ei->ce_value_fixed_initial = getFile16BitBE(file);
3288   ei->ce_value_random_initial = getFile16BitBE(file);
3289   ei->use_last_ce_value = getFile8Bit(file);
3290
3291   ei->use_gfx_element = getFile8Bit(file);
3292   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3293
3294   ei->collect_score_initial = getFile8Bit(file);
3295   ei->collect_count_initial = getFile8Bit(file);
3296
3297   ei->drop_delay_fixed = getFile8Bit(file);
3298   ei->push_delay_fixed = getFile8Bit(file);
3299   ei->drop_delay_random = getFile8Bit(file);
3300   ei->push_delay_random = getFile8Bit(file);
3301   ei->move_delay_fixed = getFile16BitBE(file);
3302   ei->move_delay_random = getFile16BitBE(file);
3303
3304   // bits 0 - 15 of "move_pattern" ...
3305   ei->move_pattern = getFile16BitBE(file);
3306   ei->move_direction_initial = getFile8Bit(file);
3307   ei->move_stepsize = getFile8Bit(file);
3308
3309   ei->slippery_type = getFile8Bit(file);
3310
3311   for (y = 0; y < 3; y++)
3312     for (x = 0; x < 3; x++)
3313       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3314
3315   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3316   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3317   ei->move_leave_type = getFile8Bit(file);
3318
3319   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3320   ei->move_pattern |= (getFile16BitBE(file) << 16);
3321
3322   ei->access_direction = getFile8Bit(file);
3323
3324   ei->explosion_delay = getFile8Bit(file);
3325   ei->ignition_delay = getFile8Bit(file);
3326   ei->explosion_type = getFile8Bit(file);
3327
3328   // some free bytes for future custom property values and padding
3329   ReadUnusedBytesFromFile(file, 1);
3330
3331   // ---------- change page property values (48 bytes) ------------------------
3332
3333   setElementChangePages(ei, ei->num_change_pages);
3334
3335   for (i = 0; i < ei->num_change_pages; i++)
3336   {
3337     struct ElementChangeInfo *change = &ei->change_page[i];
3338     unsigned int event_bits;
3339
3340     // always start with reliable default values
3341     setElementChangeInfoToDefaults(change);
3342
3343     // bits 0 - 31 of "has_event[]" ...
3344     event_bits = getFile32BitBE(file);
3345     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3346       if (event_bits & (1u << j))
3347         change->has_event[j] = TRUE;
3348
3349     change->target_element = getMappedElement(getFile16BitBE(file));
3350
3351     change->delay_fixed = getFile16BitBE(file);
3352     change->delay_random = getFile16BitBE(file);
3353     change->delay_frames = getFile16BitBE(file);
3354
3355     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3356
3357     change->explode = getFile8Bit(file);
3358     change->use_target_content = getFile8Bit(file);
3359     change->only_if_complete = getFile8Bit(file);
3360     change->use_random_replace = getFile8Bit(file);
3361
3362     change->random_percentage = getFile8Bit(file);
3363     change->replace_when = getFile8Bit(file);
3364
3365     for (y = 0; y < 3; y++)
3366       for (x = 0; x < 3; x++)
3367         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3368
3369     change->can_change = getFile8Bit(file);
3370
3371     change->trigger_side = getFile8Bit(file);
3372
3373     change->trigger_player = getFile8Bit(file);
3374     change->trigger_page = getFile8Bit(file);
3375
3376     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3377                             CH_PAGE_ANY : (1 << change->trigger_page));
3378
3379     change->has_action = getFile8Bit(file);
3380     change->action_type = getFile8Bit(file);
3381     change->action_mode = getFile8Bit(file);
3382     change->action_arg = getFile16BitBE(file);
3383
3384     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3385     event_bits = getFile8Bit(file);
3386     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3387       if (event_bits & (1u << (j - 32)))
3388         change->has_event[j] = TRUE;
3389   }
3390
3391   // mark this custom element as modified
3392   ei->modified_settings = TRUE;
3393
3394   level->file_has_custom_elements = TRUE;
3395
3396   return chunk_size;
3397 }
3398
3399 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3400 {
3401   struct ElementInfo *ei;
3402   struct ElementGroupInfo *group;
3403   int element;
3404   int i;
3405
3406   element = getMappedElement(getFile16BitBE(file));
3407
3408   if (!IS_GROUP_ELEMENT(element))
3409   {
3410     Warn("invalid group element number %d", element);
3411
3412     ReadUnusedBytesFromFile(file, chunk_size - 2);
3413
3414     return chunk_size;
3415   }
3416
3417   ei = &element_info[element];
3418
3419   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3420     ei->description[i] = getFile8Bit(file);
3421   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3422
3423   group = element_info[element].group;
3424
3425   group->num_elements = getFile8Bit(file);
3426
3427   ei->use_gfx_element = getFile8Bit(file);
3428   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3429
3430   group->choice_mode = getFile8Bit(file);
3431
3432   // some free bytes for future values and padding
3433   ReadUnusedBytesFromFile(file, 3);
3434
3435   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3436     group->element[i] = getMappedElement(getFile16BitBE(file));
3437
3438   // mark this group element as modified
3439   element_info[element].modified_settings = TRUE;
3440
3441   level->file_has_custom_elements = TRUE;
3442
3443   return chunk_size;
3444 }
3445
3446 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3447                                 int element, int real_element)
3448 {
3449   int micro_chunk_size = 0;
3450   int conf_type = getFile8Bit(file);
3451   int byte_mask = conf_type & CONF_MASK_BYTES;
3452   boolean element_found = FALSE;
3453   int i;
3454
3455   micro_chunk_size += 1;
3456
3457   if (byte_mask == CONF_MASK_MULTI_BYTES)
3458   {
3459     int num_bytes = getFile16BitBE(file);
3460     byte *buffer = checked_malloc(num_bytes);
3461
3462     ReadBytesFromFile(file, buffer, num_bytes);
3463
3464     for (i = 0; conf[i].data_type != -1; i++)
3465     {
3466       if (conf[i].element == element &&
3467           conf[i].conf_type == conf_type)
3468       {
3469         int data_type = conf[i].data_type;
3470         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3471         int max_num_entities = conf[i].max_num_entities;
3472
3473         if (num_entities > max_num_entities)
3474         {
3475           Warn("truncating number of entities for element %d from %d to %d",
3476                element, num_entities, max_num_entities);
3477
3478           num_entities = max_num_entities;
3479         }
3480
3481         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3482                                   data_type == TYPE_CONTENT_LIST))
3483         {
3484           // for element and content lists, zero entities are not allowed
3485           Warn("found empty list of entities for element %d", element);
3486
3487           // do not set "num_entities" here to prevent reading behind buffer
3488
3489           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3490         }
3491         else
3492         {
3493           *(int *)(conf[i].num_entities) = num_entities;
3494         }
3495
3496         element_found = TRUE;
3497
3498         if (data_type == TYPE_STRING)
3499         {
3500           char *string = (char *)(conf[i].value);
3501           int j;
3502
3503           for (j = 0; j < max_num_entities; j++)
3504             string[j] = (j < num_entities ? buffer[j] : '\0');
3505         }
3506         else if (data_type == TYPE_ELEMENT_LIST)
3507         {
3508           int *element_array = (int *)(conf[i].value);
3509           int j;
3510
3511           for (j = 0; j < num_entities; j++)
3512             element_array[j] =
3513               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3514         }
3515         else if (data_type == TYPE_CONTENT_LIST)
3516         {
3517           struct Content *content= (struct Content *)(conf[i].value);
3518           int c, x, y;
3519
3520           for (c = 0; c < num_entities; c++)
3521             for (y = 0; y < 3; y++)
3522               for (x = 0; x < 3; x++)
3523                 content[c].e[x][y] =
3524                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3525         }
3526         else
3527           element_found = FALSE;
3528
3529         break;
3530       }
3531     }
3532
3533     checked_free(buffer);
3534
3535     micro_chunk_size += 2 + num_bytes;
3536   }
3537   else          // constant size configuration data (1, 2 or 4 bytes)
3538   {
3539     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3540                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3541                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3542
3543     for (i = 0; conf[i].data_type != -1; i++)
3544     {
3545       if (conf[i].element == element &&
3546           conf[i].conf_type == conf_type)
3547       {
3548         int data_type = conf[i].data_type;
3549
3550         if (data_type == TYPE_ELEMENT)
3551           value = getMappedElement(value);
3552
3553         if (data_type == TYPE_BOOLEAN)
3554           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3555         else
3556           *(int *)    (conf[i].value) = value;
3557
3558         element_found = TRUE;
3559
3560         break;
3561       }
3562     }
3563
3564     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3565   }
3566
3567   if (!element_found)
3568   {
3569     char *error_conf_chunk_bytes =
3570       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3571        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3572        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3573     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3574     int error_element = real_element;
3575
3576     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3577          error_conf_chunk_bytes, error_conf_chunk_token,
3578          error_element, EL_NAME(error_element));
3579   }
3580
3581   return micro_chunk_size;
3582 }
3583
3584 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3585 {
3586   int real_chunk_size = 0;
3587
3588   li = *level;          // copy level data into temporary buffer
3589
3590   while (!checkEndOfFile(file))
3591   {
3592     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3593
3594     if (real_chunk_size >= chunk_size)
3595       break;
3596   }
3597
3598   *level = li;          // copy temporary buffer back to level data
3599
3600   return real_chunk_size;
3601 }
3602
3603 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3604 {
3605   int real_chunk_size = 0;
3606
3607   li = *level;          // copy level data into temporary buffer
3608
3609   while (!checkEndOfFile(file))
3610   {
3611     int element = getMappedElement(getFile16BitBE(file));
3612
3613     real_chunk_size += 2;
3614     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3615                                             element, element);
3616     if (real_chunk_size >= chunk_size)
3617       break;
3618   }
3619
3620   *level = li;          // copy temporary buffer back to level data
3621
3622   return real_chunk_size;
3623 }
3624
3625 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3626 {
3627   int real_chunk_size = 0;
3628
3629   li = *level;          // copy level data into temporary buffer
3630
3631   while (!checkEndOfFile(file))
3632   {
3633     int element = getMappedElement(getFile16BitBE(file));
3634
3635     real_chunk_size += 2;
3636     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3637                                             element, element);
3638     if (real_chunk_size >= chunk_size)
3639       break;
3640   }
3641
3642   *level = li;          // copy temporary buffer back to level data
3643
3644   return real_chunk_size;
3645 }
3646
3647 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3648 {
3649   int element = getMappedElement(getFile16BitBE(file));
3650   int envelope_nr = element - EL_ENVELOPE_1;
3651   int real_chunk_size = 2;
3652
3653   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3654
3655   while (!checkEndOfFile(file))
3656   {
3657     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3658                                             -1, element);
3659
3660     if (real_chunk_size >= chunk_size)
3661       break;
3662   }
3663
3664   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3665
3666   return real_chunk_size;
3667 }
3668
3669 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3670 {
3671   int element = getMappedElement(getFile16BitBE(file));
3672   int real_chunk_size = 2;
3673   struct ElementInfo *ei = &element_info[element];
3674   int i;
3675
3676   xx_ei = *ei;          // copy element data into temporary buffer
3677
3678   xx_ei.num_change_pages = -1;
3679
3680   while (!checkEndOfFile(file))
3681   {
3682     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3683                                             -1, element);
3684     if (xx_ei.num_change_pages != -1)
3685       break;
3686
3687     if (real_chunk_size >= chunk_size)
3688       break;
3689   }
3690
3691   *ei = xx_ei;
3692
3693   if (ei->num_change_pages == -1)
3694   {
3695     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3696          EL_NAME(element));
3697
3698     ei->num_change_pages = 1;
3699
3700     setElementChangePages(ei, 1);
3701     setElementChangeInfoToDefaults(ei->change);
3702
3703     return real_chunk_size;
3704   }
3705
3706   // initialize number of change pages stored for this custom element
3707   setElementChangePages(ei, ei->num_change_pages);
3708   for (i = 0; i < ei->num_change_pages; i++)
3709     setElementChangeInfoToDefaults(&ei->change_page[i]);
3710
3711   // start with reading properties for the first change page
3712   xx_current_change_page = 0;
3713
3714   while (!checkEndOfFile(file))
3715   {
3716     // level file might contain invalid change page number
3717     if (xx_current_change_page >= ei->num_change_pages)
3718       break;
3719
3720     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3721
3722     xx_change = *change;        // copy change data into temporary buffer
3723
3724     resetEventBits();           // reset bits; change page might have changed
3725
3726     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3727                                             -1, element);
3728
3729     *change = xx_change;
3730
3731     setEventFlagsFromEventBits(change);
3732
3733     if (real_chunk_size >= chunk_size)
3734       break;
3735   }
3736
3737   level->file_has_custom_elements = TRUE;
3738
3739   return real_chunk_size;
3740 }
3741
3742 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3743 {
3744   int element = getMappedElement(getFile16BitBE(file));
3745   int real_chunk_size = 2;
3746   struct ElementInfo *ei = &element_info[element];
3747   struct ElementGroupInfo *group = ei->group;
3748
3749   if (group == NULL)
3750     return -1;
3751
3752   xx_ei = *ei;          // copy element data into temporary buffer
3753   xx_group = *group;    // copy group data into temporary buffer
3754
3755   while (!checkEndOfFile(file))
3756   {
3757     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3758                                             -1, element);
3759
3760     if (real_chunk_size >= chunk_size)
3761       break;
3762   }
3763
3764   *ei = xx_ei;
3765   *group = xx_group;
3766
3767   level->file_has_custom_elements = TRUE;
3768
3769   return real_chunk_size;
3770 }
3771
3772 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3773 {
3774   int element = getMappedElement(getFile16BitBE(file));
3775   int real_chunk_size = 2;
3776   struct ElementInfo *ei = &element_info[element];
3777
3778   xx_ei = *ei;          // copy element data into temporary buffer
3779
3780   while (!checkEndOfFile(file))
3781   {
3782     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3783                                             -1, element);
3784
3785     if (real_chunk_size >= chunk_size)
3786       break;
3787   }
3788
3789   *ei = xx_ei;
3790
3791   level->file_has_custom_elements = TRUE;
3792
3793   return real_chunk_size;
3794 }
3795
3796 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3797                                       struct LevelFileInfo *level_file_info,
3798                                       boolean level_info_only)
3799 {
3800   char *filename = level_file_info->filename;
3801   char cookie[MAX_LINE_LEN];
3802   char chunk_name[CHUNK_ID_LEN + 1];
3803   int chunk_size;
3804   File *file;
3805
3806   if (!(file = openFile(filename, MODE_READ)))
3807   {
3808     level->no_valid_file = TRUE;
3809     level->no_level_file = TRUE;
3810
3811     if (level_info_only)
3812       return;
3813
3814     Warn("cannot read level '%s' -- using empty level", filename);
3815
3816     if (!setup.editor.use_template_for_new_levels)
3817       return;
3818
3819     // if level file not found, try to initialize level data from template
3820     filename = getGlobalLevelTemplateFilename();
3821
3822     if (!(file = openFile(filename, MODE_READ)))
3823       return;
3824
3825     // default: for empty levels, use level template for custom elements
3826     level->use_custom_template = TRUE;
3827
3828     level->no_valid_file = FALSE;
3829   }
3830
3831   getFileChunkBE(file, chunk_name, NULL);
3832   if (strEqual(chunk_name, "RND1"))
3833   {
3834     getFile32BitBE(file);               // not used
3835
3836     getFileChunkBE(file, chunk_name, NULL);
3837     if (!strEqual(chunk_name, "CAVE"))
3838     {
3839       level->no_valid_file = TRUE;
3840
3841       Warn("unknown format of level file '%s'", filename);
3842
3843       closeFile(file);
3844
3845       return;
3846     }
3847   }
3848   else  // check for pre-2.0 file format with cookie string
3849   {
3850     strcpy(cookie, chunk_name);
3851     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3852       cookie[4] = '\0';
3853     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3854       cookie[strlen(cookie) - 1] = '\0';
3855
3856     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3857     {
3858       level->no_valid_file = TRUE;
3859
3860       Warn("unknown format of level file '%s'", filename);
3861
3862       closeFile(file);
3863
3864       return;
3865     }
3866
3867     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3868     {
3869       level->no_valid_file = TRUE;
3870
3871       Warn("unsupported version of level file '%s'", filename);
3872
3873       closeFile(file);
3874
3875       return;
3876     }
3877
3878     // pre-2.0 level files have no game version, so use file version here
3879     level->game_version = level->file_version;
3880   }
3881
3882   if (level->file_version < FILE_VERSION_1_2)
3883   {
3884     // level files from versions before 1.2.0 without chunk structure
3885     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3886     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3887   }
3888   else
3889   {
3890     static struct
3891     {
3892       char *name;
3893       int size;
3894       int (*loader)(File *, int, struct LevelInfo *);
3895     }
3896     chunk_info[] =
3897     {
3898       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3899       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3900       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3901       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3902       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3903       { "INFO", -1,                     LoadLevel_INFO },
3904       { "BODY", -1,                     LoadLevel_BODY },
3905       { "CONT", -1,                     LoadLevel_CONT },
3906       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3907       { "CNT3", -1,                     LoadLevel_CNT3 },
3908       { "CUS1", -1,                     LoadLevel_CUS1 },
3909       { "CUS2", -1,                     LoadLevel_CUS2 },
3910       { "CUS3", -1,                     LoadLevel_CUS3 },
3911       { "CUS4", -1,                     LoadLevel_CUS4 },
3912       { "GRP1", -1,                     LoadLevel_GRP1 },
3913       { "CONF", -1,                     LoadLevel_CONF },
3914       { "ELEM", -1,                     LoadLevel_ELEM },
3915       { "NOTE", -1,                     LoadLevel_NOTE },
3916       { "CUSX", -1,                     LoadLevel_CUSX },
3917       { "GRPX", -1,                     LoadLevel_GRPX },
3918       { "EMPX", -1,                     LoadLevel_EMPX },
3919
3920       {  NULL,  0,                      NULL }
3921     };
3922
3923     while (getFileChunkBE(file, chunk_name, &chunk_size))
3924     {
3925       int i = 0;
3926
3927       while (chunk_info[i].name != NULL &&
3928              !strEqual(chunk_name, chunk_info[i].name))
3929         i++;
3930
3931       if (chunk_info[i].name == NULL)
3932       {
3933         Warn("unknown chunk '%s' in level file '%s'",
3934              chunk_name, filename);
3935
3936         ReadUnusedBytesFromFile(file, chunk_size);
3937       }
3938       else if (chunk_info[i].size != -1 &&
3939                chunk_info[i].size != chunk_size)
3940       {
3941         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3942              chunk_size, chunk_name, filename);
3943
3944         ReadUnusedBytesFromFile(file, chunk_size);
3945       }
3946       else
3947       {
3948         // call function to load this level chunk
3949         int chunk_size_expected =
3950           (chunk_info[i].loader)(file, chunk_size, level);
3951
3952         if (chunk_size_expected < 0)
3953         {
3954           Warn("error reading chunk '%s' in level file '%s'",
3955                chunk_name, filename);
3956
3957           break;
3958         }
3959
3960         // the size of some chunks cannot be checked before reading other
3961         // chunks first (like "HEAD" and "BODY") that contain some header
3962         // information, so check them here
3963         if (chunk_size_expected != chunk_size)
3964         {
3965           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3966                chunk_size, chunk_name, filename);
3967
3968           break;
3969         }
3970       }
3971     }
3972   }
3973
3974   closeFile(file);
3975 }
3976
3977
3978 // ----------------------------------------------------------------------------
3979 // functions for loading BD level
3980 // ----------------------------------------------------------------------------
3981
3982 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3983 {
3984   struct LevelInfo_BD *level_bd = level->native_bd_level;
3985   GdCave *cave = NULL;  // will be changed below
3986   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3987   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3988   int x, y;
3989
3990   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3991
3992   // cave and map newly allocated when set to defaults above
3993   cave = level_bd->cave;
3994
3995   // level type
3996   cave->intermission                    = level->bd_intermission;
3997
3998   // level settings
3999   cave->level_time[0]                   = level->time;
4000   cave->level_diamonds[0]               = level->gems_needed;
4001
4002   // game timing
4003   cave->scheduling                      = level->bd_scheduling_type;
4004   cave->pal_timing                      = level->bd_pal_timing;
4005   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4006   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4007   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4008   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4009
4010   // scores
4011   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4012   cave->diamond_value                   = level->score[SC_EMERALD];
4013   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4014
4015   // compatibility settings
4016   cave->lineshift                       = level->bd_line_shifting_borders;
4017   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4018   cave->short_explosions                = level->bd_short_explosions;
4019   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4020
4021   // player properties
4022   cave->diagonal_movements              = level->bd_diagonal_movements;
4023   cave->active_is_first_found           = level->bd_topmost_player_active;
4024   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4025   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4026   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4027   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4028
4029   // element properties
4030   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4031   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4032   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4033   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4034   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4035   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4036   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4037   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4038   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4039   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4040   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4041   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4042   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4043   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4044   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4045   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4046   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4047   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4048   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4049   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4050
4051   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4052   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4053   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4054   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4055   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4056   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4057
4058   cave->slime_predictable               = level->bd_slime_is_predictable;
4059   cave->slime_correct_random            = level->bd_slime_correct_random;
4060   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4061   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4062   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4063   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4064
4065   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4066   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4067   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4068
4069   cave->biter_delay_frame               = level->bd_biter_move_delay;
4070   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4071
4072   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4073
4074   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4075
4076   cave->replicators_active              = level->bd_replicators_active;
4077   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4078
4079   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4080   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4081
4082   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4083
4084   // level name
4085   strncpy(cave->name, level->name, sizeof(GdString));
4086   cave->name[sizeof(GdString) - 1] = '\0';
4087
4088   // playfield elements
4089   for (x = 0; x < cave->w; x++)
4090     for (y = 0; y < cave->h; y++)
4091       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4092 }
4093
4094 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4095 {
4096   struct LevelInfo_BD *level_bd = level->native_bd_level;
4097   GdCave *cave = level_bd->cave;
4098   int bd_level_nr = level_bd->level_nr;
4099   int x, y;
4100
4101   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4102   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4103
4104   // level type
4105   level->bd_intermission                = cave->intermission;
4106
4107   // level settings
4108   level->time                           = cave->level_time[bd_level_nr];
4109   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4110
4111   // game timing
4112   level->bd_scheduling_type             = cave->scheduling;
4113   level->bd_pal_timing                  = cave->pal_timing;
4114   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4115   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4116   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4117   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4118
4119   // scores
4120   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4121   level->score[SC_EMERALD]              = cave->diamond_value;
4122   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4123
4124   // compatibility settings
4125   level->bd_line_shifting_borders       = cave->lineshift;
4126   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4127   level->bd_short_explosions            = cave->short_explosions;
4128   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4129
4130   // player properties
4131   level->bd_diagonal_movements          = cave->diagonal_movements;
4132   level->bd_topmost_player_active       = cave->active_is_first_found;
4133   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4134   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4135   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4136   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4137
4138   // element properties
4139   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4140   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4141   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4142   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4143   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4144   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4145   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4146   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4147   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4148   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4149   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4150   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4151   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4152   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4153   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4154   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4155   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4156   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4157   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4158   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4159
4160   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4161   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4162   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4163   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4164   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4165   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4166
4167   level->bd_slime_is_predictable        = cave->slime_predictable;
4168   level->bd_slime_correct_random        = cave->slime_correct_random;
4169   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4170   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4171   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4172   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4173
4174   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4175   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4176   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4177
4178   level->bd_biter_move_delay            = cave->biter_delay_frame;
4179   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4180
4181   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4182
4183   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4184
4185   level->bd_replicators_active          = cave->replicators_active;
4186   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4187
4188   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4189   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4190
4191   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4192
4193   // level name
4194   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4195
4196   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4197   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4198
4199   // playfield elements
4200   for (x = 0; x < level->fieldx; x++)
4201     for (y = 0; y < level->fieldy; y++)
4202       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4203
4204   checked_free(cave_name);
4205 }
4206
4207 static void setTapeInfoToDefaults(void);
4208
4209 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4210 {
4211   struct LevelInfo_BD *level_bd = level->native_bd_level;
4212   GdCave *cave = level_bd->cave;
4213   GdReplay *replay = level_bd->replay;
4214   int i;
4215
4216   if (replay == NULL)
4217     return;
4218
4219   // always start with reliable default values
4220   setTapeInfoToDefaults();
4221
4222   tape.level_nr = level_nr;             // (currently not used)
4223   tape.random_seed = replay->seed;
4224
4225   TapeSetDateFromIsoDateString(replay->date);
4226
4227   tape.counter = 0;
4228   tape.pos[tape.counter].delay = 0;
4229
4230   tape.bd_replay = TRUE;
4231
4232   // all time calculations only used to display approximate tape time
4233   int cave_speed = cave->speed;
4234   int milliseconds_game = 0;
4235   int milliseconds_elapsed = 20;
4236
4237   for (i = 0; i < replay->movements->len; i++)
4238   {
4239     int replay_action = replay->movements->data[i];
4240     int tape_action = map_action_BD_to_RND(replay_action);
4241     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4242     boolean success = 0;
4243
4244     while (1)
4245     {
4246       success = TapeAddAction(action);
4247
4248       milliseconds_game += milliseconds_elapsed;
4249
4250       if (milliseconds_game >= cave_speed)
4251       {
4252         milliseconds_game -= cave_speed;
4253
4254         break;
4255       }
4256     }
4257
4258     tape.counter++;
4259     tape.pos[tape.counter].delay = 0;
4260     tape.pos[tape.counter].action[0] = 0;
4261
4262     if (!success)
4263     {
4264       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4265
4266       break;
4267     }
4268   }
4269
4270   TapeHaltRecording();
4271 }
4272
4273
4274 // ----------------------------------------------------------------------------
4275 // functions for loading EM level
4276 // ----------------------------------------------------------------------------
4277
4278 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4279 {
4280   static int ball_xy[8][2] =
4281   {
4282     { 0, 0 },
4283     { 1, 0 },
4284     { 2, 0 },
4285     { 0, 1 },
4286     { 2, 1 },
4287     { 0, 2 },
4288     { 1, 2 },
4289     { 2, 2 },
4290   };
4291   struct LevelInfo_EM *level_em = level->native_em_level;
4292   struct CAVE *cav = level_em->cav;
4293   int i, j, x, y;
4294
4295   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4296   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4297
4298   cav->time_seconds     = level->time;
4299   cav->gems_needed      = level->gems_needed;
4300
4301   cav->emerald_score    = level->score[SC_EMERALD];
4302   cav->diamond_score    = level->score[SC_DIAMOND];
4303   cav->alien_score      = level->score[SC_ROBOT];
4304   cav->tank_score       = level->score[SC_SPACESHIP];
4305   cav->bug_score        = level->score[SC_BUG];
4306   cav->eater_score      = level->score[SC_YAMYAM];
4307   cav->nut_score        = level->score[SC_NUT];
4308   cav->dynamite_score   = level->score[SC_DYNAMITE];
4309   cav->key_score        = level->score[SC_KEY];
4310   cav->exit_score       = level->score[SC_TIME_BONUS];
4311
4312   cav->num_eater_arrays = level->num_yamyam_contents;
4313
4314   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4315     for (y = 0; y < 3; y++)
4316       for (x = 0; x < 3; x++)
4317         cav->eater_array[i][y * 3 + x] =
4318           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4319
4320   cav->amoeba_time              = level->amoeba_speed;
4321   cav->wonderwall_time          = level->time_magic_wall;
4322   cav->wheel_time               = level->time_wheel;
4323
4324   cav->android_move_time        = level->android_move_time;
4325   cav->android_clone_time       = level->android_clone_time;
4326   cav->ball_random              = level->ball_random;
4327   cav->ball_active              = level->ball_active_initial;
4328   cav->ball_time                = level->ball_time;
4329   cav->num_ball_arrays          = level->num_ball_contents;
4330
4331   cav->lenses_score             = level->lenses_score;
4332   cav->magnify_score            = level->magnify_score;
4333   cav->slurp_score              = level->slurp_score;
4334
4335   cav->lenses_time              = level->lenses_time;
4336   cav->magnify_time             = level->magnify_time;
4337
4338   cav->wind_time = 9999;
4339   cav->wind_direction =
4340     map_direction_RND_to_EM(level->wind_direction_initial);
4341
4342   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4343     for (j = 0; j < 8; j++)
4344       cav->ball_array[i][j] =
4345         map_element_RND_to_EM_cave(level->ball_content[i].
4346                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4347
4348   map_android_clone_elements_RND_to_EM(level);
4349
4350   // first fill the complete playfield with the empty space element
4351   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4352     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4353       cav->cave[x][y] = Cblank;
4354
4355   // then copy the real level contents from level file into the playfield
4356   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4357   {
4358     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4359
4360     if (level->field[x][y] == EL_AMOEBA_DEAD)
4361       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4362
4363     cav->cave[x][y] = new_element;
4364   }
4365
4366   for (i = 0; i < MAX_PLAYERS; i++)
4367   {
4368     cav->player_x[i] = -1;
4369     cav->player_y[i] = -1;
4370   }
4371
4372   // initialize player positions and delete players from the playfield
4373   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4374   {
4375     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4376     {
4377       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4378
4379       cav->player_x[player_nr] = x;
4380       cav->player_y[player_nr] = y;
4381
4382       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4383     }
4384   }
4385 }
4386
4387 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4388 {
4389   static int ball_xy[8][2] =
4390   {
4391     { 0, 0 },
4392     { 1, 0 },
4393     { 2, 0 },
4394     { 0, 1 },
4395     { 2, 1 },
4396     { 0, 2 },
4397     { 1, 2 },
4398     { 2, 2 },
4399   };
4400   struct LevelInfo_EM *level_em = level->native_em_level;
4401   struct CAVE *cav = level_em->cav;
4402   int i, j, x, y;
4403
4404   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4405   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4406
4407   level->time        = cav->time_seconds;
4408   level->gems_needed = cav->gems_needed;
4409
4410   sprintf(level->name, "Level %d", level->file_info.nr);
4411
4412   level->score[SC_EMERALD]      = cav->emerald_score;
4413   level->score[SC_DIAMOND]      = cav->diamond_score;
4414   level->score[SC_ROBOT]        = cav->alien_score;
4415   level->score[SC_SPACESHIP]    = cav->tank_score;
4416   level->score[SC_BUG]          = cav->bug_score;
4417   level->score[SC_YAMYAM]       = cav->eater_score;
4418   level->score[SC_NUT]          = cav->nut_score;
4419   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4420   level->score[SC_KEY]          = cav->key_score;
4421   level->score[SC_TIME_BONUS]   = cav->exit_score;
4422
4423   level->num_yamyam_contents    = cav->num_eater_arrays;
4424
4425   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4426     for (y = 0; y < 3; y++)
4427       for (x = 0; x < 3; x++)
4428         level->yamyam_content[i].e[x][y] =
4429           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4430
4431   level->amoeba_speed           = cav->amoeba_time;
4432   level->time_magic_wall        = cav->wonderwall_time;
4433   level->time_wheel             = cav->wheel_time;
4434
4435   level->android_move_time      = cav->android_move_time;
4436   level->android_clone_time     = cav->android_clone_time;
4437   level->ball_random            = cav->ball_random;
4438   level->ball_active_initial    = cav->ball_active;
4439   level->ball_time              = cav->ball_time;
4440   level->num_ball_contents      = cav->num_ball_arrays;
4441
4442   level->lenses_score           = cav->lenses_score;
4443   level->magnify_score          = cav->magnify_score;
4444   level->slurp_score            = cav->slurp_score;
4445
4446   level->lenses_time            = cav->lenses_time;
4447   level->magnify_time           = cav->magnify_time;
4448
4449   level->wind_direction_initial =
4450     map_direction_EM_to_RND(cav->wind_direction);
4451
4452   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4453     for (j = 0; j < 8; j++)
4454       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4455         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4456
4457   map_android_clone_elements_EM_to_RND(level);
4458
4459   // convert the playfield (some elements need special treatment)
4460   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4461   {
4462     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4463
4464     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4465       new_element = EL_AMOEBA_DEAD;
4466
4467     level->field[x][y] = new_element;
4468   }
4469
4470   for (i = 0; i < MAX_PLAYERS; i++)
4471   {
4472     // in case of all players set to the same field, use the first player
4473     int nr = MAX_PLAYERS - i - 1;
4474     int jx = cav->player_x[nr];
4475     int jy = cav->player_y[nr];
4476
4477     if (jx != -1 && jy != -1)
4478       level->field[jx][jy] = EL_PLAYER_1 + nr;
4479   }
4480
4481   // time score is counted for each 10 seconds left in Emerald Mine levels
4482   level->time_score_base = 10;
4483 }
4484
4485
4486 // ----------------------------------------------------------------------------
4487 // functions for loading SP level
4488 // ----------------------------------------------------------------------------
4489
4490 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4491 {
4492   struct LevelInfo_SP *level_sp = level->native_sp_level;
4493   LevelInfoType *header = &level_sp->header;
4494   int i, x, y;
4495
4496   level_sp->width  = level->fieldx;
4497   level_sp->height = level->fieldy;
4498
4499   for (x = 0; x < level->fieldx; x++)
4500     for (y = 0; y < level->fieldy; y++)
4501       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4502
4503   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4504
4505   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4506     header->LevelTitle[i] = level->name[i];
4507   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4508
4509   header->InfotronsNeeded = level->gems_needed;
4510
4511   header->SpecialPortCount = 0;
4512
4513   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4514   {
4515     boolean gravity_port_found = FALSE;
4516     boolean gravity_port_valid = FALSE;
4517     int gravity_port_flag;
4518     int gravity_port_base_element;
4519     int element = level->field[x][y];
4520
4521     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4522         element <= EL_SP_GRAVITY_ON_PORT_UP)
4523     {
4524       gravity_port_found = TRUE;
4525       gravity_port_valid = TRUE;
4526       gravity_port_flag = 1;
4527       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4528     }
4529     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4530              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4531     {
4532       gravity_port_found = TRUE;
4533       gravity_port_valid = TRUE;
4534       gravity_port_flag = 0;
4535       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4536     }
4537     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4538              element <= EL_SP_GRAVITY_PORT_UP)
4539     {
4540       // change R'n'D style gravity inverting special port to normal port
4541       // (there are no gravity inverting ports in native Supaplex engine)
4542
4543       gravity_port_found = TRUE;
4544       gravity_port_valid = FALSE;
4545       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4546     }
4547
4548     if (gravity_port_found)
4549     {
4550       if (gravity_port_valid &&
4551           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4552       {
4553         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4554
4555         port->PortLocation = (y * level->fieldx + x) * 2;
4556         port->Gravity = gravity_port_flag;
4557
4558         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4559
4560         header->SpecialPortCount++;
4561       }
4562       else
4563       {
4564         // change special gravity port to normal port
4565
4566         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4567       }
4568
4569       level_sp->playfield[x][y] = element - EL_SP_START;
4570     }
4571   }
4572 }
4573
4574 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4575 {
4576   struct LevelInfo_SP *level_sp = level->native_sp_level;
4577   LevelInfoType *header = &level_sp->header;
4578   boolean num_invalid_elements = 0;
4579   int i, j, x, y;
4580
4581   level->fieldx = level_sp->width;
4582   level->fieldy = level_sp->height;
4583
4584   for (x = 0; x < level->fieldx; x++)
4585   {
4586     for (y = 0; y < level->fieldy; y++)
4587     {
4588       int element_old = level_sp->playfield[x][y];
4589       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4590
4591       if (element_new == EL_UNKNOWN)
4592       {
4593         num_invalid_elements++;
4594
4595         Debug("level:native:SP", "invalid element %d at position %d, %d",
4596               element_old, x, y);
4597       }
4598
4599       level->field[x][y] = element_new;
4600     }
4601   }
4602
4603   if (num_invalid_elements > 0)
4604     Warn("found %d invalid elements%s", num_invalid_elements,
4605          (!options.debug ? " (use '--debug' for more details)" : ""));
4606
4607   for (i = 0; i < MAX_PLAYERS; i++)
4608     level->initial_player_gravity[i] =
4609       (header->InitialGravity == 1 ? TRUE : FALSE);
4610
4611   // skip leading spaces
4612   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4613     if (header->LevelTitle[i] != ' ')
4614       break;
4615
4616   // copy level title
4617   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4618     level->name[j] = header->LevelTitle[i];
4619   level->name[j] = '\0';
4620
4621   // cut trailing spaces
4622   for (; j > 0; j--)
4623     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4624       level->name[j - 1] = '\0';
4625
4626   level->gems_needed = header->InfotronsNeeded;
4627
4628   for (i = 0; i < header->SpecialPortCount; i++)
4629   {
4630     SpecialPortType *port = &header->SpecialPort[i];
4631     int port_location = port->PortLocation;
4632     int gravity = port->Gravity;
4633     int port_x, port_y, port_element;
4634
4635     port_x = (port_location / 2) % level->fieldx;
4636     port_y = (port_location / 2) / level->fieldx;
4637
4638     if (port_x < 0 || port_x >= level->fieldx ||
4639         port_y < 0 || port_y >= level->fieldy)
4640     {
4641       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4642
4643       continue;
4644     }
4645
4646     port_element = level->field[port_x][port_y];
4647
4648     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4649         port_element > EL_SP_GRAVITY_PORT_UP)
4650     {
4651       Warn("no special port at position (%d, %d)", port_x, port_y);
4652
4653       continue;
4654     }
4655
4656     // change previous (wrong) gravity inverting special port to either
4657     // gravity enabling special port or gravity disabling special port
4658     level->field[port_x][port_y] +=
4659       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4660        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4661   }
4662
4663   // change special gravity ports without database entries to normal ports
4664   for (x = 0; x < level->fieldx; x++)
4665     for (y = 0; y < level->fieldy; y++)
4666       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4667           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4668         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4669
4670   level->time = 0;                      // no time limit
4671   level->amoeba_speed = 0;
4672   level->time_magic_wall = 0;
4673   level->time_wheel = 0;
4674   level->amoeba_content = EL_EMPTY;
4675
4676   // original Supaplex does not use score values -- rate by playing time
4677   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4678     level->score[i] = 0;
4679
4680   level->rate_time_over_score = TRUE;
4681
4682   // there are no yamyams in supaplex levels
4683   for (i = 0; i < level->num_yamyam_contents; i++)
4684     for (x = 0; x < 3; x++)
4685       for (y = 0; y < 3; y++)
4686         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4687 }
4688
4689 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4690 {
4691   struct LevelInfo_SP *level_sp = level->native_sp_level;
4692   struct DemoInfo_SP *demo = &level_sp->demo;
4693   int i, j;
4694
4695   // always start with reliable default values
4696   demo->is_available = FALSE;
4697   demo->length = 0;
4698
4699   if (TAPE_IS_EMPTY(tape))
4700     return;
4701
4702   demo->level_nr = tape.level_nr;       // (currently not used)
4703
4704   level_sp->header.DemoRandomSeed = tape.random_seed;
4705
4706   demo->length = 0;
4707
4708   for (i = 0; i < tape.length; i++)
4709   {
4710     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4711     int demo_repeat = tape.pos[i].delay;
4712     int demo_entries = (demo_repeat + 15) / 16;
4713
4714     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4715     {
4716       Warn("tape truncated: size exceeds maximum SP demo size %d",
4717            SP_MAX_TAPE_LEN);
4718
4719       break;
4720     }
4721
4722     for (j = 0; j < demo_repeat / 16; j++)
4723       demo->data[demo->length++] = 0xf0 | demo_action;
4724
4725     if (demo_repeat % 16)
4726       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4727   }
4728
4729   demo->is_available = TRUE;
4730 }
4731
4732 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4733 {
4734   struct LevelInfo_SP *level_sp = level->native_sp_level;
4735   struct DemoInfo_SP *demo = &level_sp->demo;
4736   char *filename = level->file_info.filename;
4737   int i;
4738
4739   // always start with reliable default values
4740   setTapeInfoToDefaults();
4741
4742   if (!demo->is_available)
4743     return;
4744
4745   tape.level_nr = demo->level_nr;       // (currently not used)
4746   tape.random_seed = level_sp->header.DemoRandomSeed;
4747
4748   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4749
4750   tape.counter = 0;
4751   tape.pos[tape.counter].delay = 0;
4752
4753   for (i = 0; i < demo->length; i++)
4754   {
4755     int demo_action = demo->data[i] & 0x0f;
4756     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4757     int tape_action = map_key_SP_to_RND(demo_action);
4758     int tape_repeat = demo_repeat + 1;
4759     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4760     boolean success = 0;
4761     int j;
4762
4763     for (j = 0; j < tape_repeat; j++)
4764       success = TapeAddAction(action);
4765
4766     if (!success)
4767     {
4768       Warn("SP demo truncated: size exceeds maximum tape size %d",
4769            MAX_TAPE_LEN);
4770
4771       break;
4772     }
4773   }
4774
4775   TapeHaltRecording();
4776 }
4777
4778
4779 // ----------------------------------------------------------------------------
4780 // functions for loading MM level
4781 // ----------------------------------------------------------------------------
4782
4783 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4784 {
4785   struct LevelInfo_MM *level_mm = level->native_mm_level;
4786   int i, x, y;
4787
4788   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4789   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4790
4791   level_mm->time = level->time;
4792   level_mm->kettles_needed = level->gems_needed;
4793   level_mm->auto_count_kettles = level->auto_count_gems;
4794
4795   level_mm->mm_laser_red   = level->mm_laser_red;
4796   level_mm->mm_laser_green = level->mm_laser_green;
4797   level_mm->mm_laser_blue  = level->mm_laser_blue;
4798
4799   level_mm->df_laser_red   = level->df_laser_red;
4800   level_mm->df_laser_green = level->df_laser_green;
4801   level_mm->df_laser_blue  = level->df_laser_blue;
4802
4803   strcpy(level_mm->name, level->name);
4804   strcpy(level_mm->author, level->author);
4805
4806   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4807   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4808   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4809   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4810   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4811
4812   level_mm->amoeba_speed = level->amoeba_speed;
4813   level_mm->time_fuse    = level->mm_time_fuse;
4814   level_mm->time_bomb    = level->mm_time_bomb;
4815   level_mm->time_ball    = level->mm_time_ball;
4816   level_mm->time_block   = level->mm_time_block;
4817
4818   level_mm->num_ball_contents = level->num_mm_ball_contents;
4819   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4820   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4821   level_mm->explode_ball = level->explode_mm_ball;
4822
4823   for (i = 0; i < level->num_mm_ball_contents; i++)
4824     level_mm->ball_content[i] =
4825       map_element_RND_to_MM(level->mm_ball_content[i]);
4826
4827   for (x = 0; x < level->fieldx; x++)
4828     for (y = 0; y < level->fieldy; y++)
4829       Ur[x][y] =
4830         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4831 }
4832
4833 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4834 {
4835   struct LevelInfo_MM *level_mm = level->native_mm_level;
4836   int i, x, y;
4837
4838   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4839   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4840
4841   level->time = level_mm->time;
4842   level->gems_needed = level_mm->kettles_needed;
4843   level->auto_count_gems = level_mm->auto_count_kettles;
4844
4845   level->mm_laser_red   = level_mm->mm_laser_red;
4846   level->mm_laser_green = level_mm->mm_laser_green;
4847   level->mm_laser_blue  = level_mm->mm_laser_blue;
4848
4849   level->df_laser_red   = level_mm->df_laser_red;
4850   level->df_laser_green = level_mm->df_laser_green;
4851   level->df_laser_blue  = level_mm->df_laser_blue;
4852
4853   strcpy(level->name, level_mm->name);
4854
4855   // only overwrite author from 'levelinfo.conf' if author defined in level
4856   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4857     strcpy(level->author, level_mm->author);
4858
4859   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4860   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4861   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4862   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4863   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4864
4865   level->amoeba_speed  = level_mm->amoeba_speed;
4866   level->mm_time_fuse  = level_mm->time_fuse;
4867   level->mm_time_bomb  = level_mm->time_bomb;
4868   level->mm_time_ball  = level_mm->time_ball;
4869   level->mm_time_block = level_mm->time_block;
4870
4871   level->num_mm_ball_contents = level_mm->num_ball_contents;
4872   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4873   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4874   level->explode_mm_ball = level_mm->explode_ball;
4875
4876   for (i = 0; i < level->num_mm_ball_contents; i++)
4877     level->mm_ball_content[i] =
4878       map_element_MM_to_RND(level_mm->ball_content[i]);
4879
4880   for (x = 0; x < level->fieldx; x++)
4881     for (y = 0; y < level->fieldy; y++)
4882       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4883 }
4884
4885
4886 // ----------------------------------------------------------------------------
4887 // functions for loading DC level
4888 // ----------------------------------------------------------------------------
4889
4890 #define DC_LEVEL_HEADER_SIZE            344
4891
4892 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4893                                         boolean init)
4894 {
4895   static int last_data_encoded;
4896   static int offset1;
4897   static int offset2;
4898   int diff;
4899   int diff_hi, diff_lo;
4900   int data_hi, data_lo;
4901   unsigned short data_decoded;
4902
4903   if (init)
4904   {
4905     last_data_encoded = 0;
4906     offset1 = -1;
4907     offset2 = 0;
4908
4909     return 0;
4910   }
4911
4912   diff = data_encoded - last_data_encoded;
4913   diff_hi = diff & ~0xff;
4914   diff_lo = diff &  0xff;
4915
4916   offset2 += diff_lo;
4917
4918   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4919   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4920   data_hi = data_hi & 0xff00;
4921
4922   data_decoded = data_hi | data_lo;
4923
4924   last_data_encoded = data_encoded;
4925
4926   offset1 = (offset1 + 1) % 31;
4927   offset2 = offset2 & 0xff;
4928
4929   return data_decoded;
4930 }
4931
4932 static int getMappedElement_DC(int element)
4933 {
4934   switch (element)
4935   {
4936     case 0x0000:
4937       element = EL_ROCK;
4938       break;
4939
4940       // 0x0117 - 0x036e: (?)
4941       // EL_DIAMOND
4942
4943       // 0x042d - 0x0684: (?)
4944       // EL_EMERALD
4945
4946     case 0x06f1:
4947       element = EL_NUT;
4948       break;
4949
4950     case 0x074c:
4951       element = EL_BOMB;
4952       break;
4953
4954     case 0x07a4:
4955       element = EL_PEARL;
4956       break;
4957
4958     case 0x0823:
4959       element = EL_CRYSTAL;
4960       break;
4961
4962     case 0x0e77:        // quicksand (boulder)
4963       element = EL_QUICKSAND_FAST_FULL;
4964       break;
4965
4966     case 0x0e99:        // slow quicksand (boulder)
4967       element = EL_QUICKSAND_FULL;
4968       break;
4969
4970     case 0x0ed2:
4971       element = EL_EM_EXIT_OPEN;
4972       break;
4973
4974     case 0x0ee3:
4975       element = EL_EM_EXIT_CLOSED;
4976       break;
4977
4978     case 0x0eeb:
4979       element = EL_EM_STEEL_EXIT_OPEN;
4980       break;
4981
4982     case 0x0efc:
4983       element = EL_EM_STEEL_EXIT_CLOSED;
4984       break;
4985
4986     case 0x0f4f:        // dynamite (lit 1)
4987       element = EL_EM_DYNAMITE_ACTIVE;
4988       break;
4989
4990     case 0x0f57:        // dynamite (lit 2)
4991       element = EL_EM_DYNAMITE_ACTIVE;
4992       break;
4993
4994     case 0x0f5f:        // dynamite (lit 3)
4995       element = EL_EM_DYNAMITE_ACTIVE;
4996       break;
4997
4998     case 0x0f67:        // dynamite (lit 4)
4999       element = EL_EM_DYNAMITE_ACTIVE;
5000       break;
5001
5002     case 0x0f81:
5003     case 0x0f82:
5004     case 0x0f83:
5005     case 0x0f84:
5006       element = EL_AMOEBA_WET;
5007       break;
5008
5009     case 0x0f85:
5010       element = EL_AMOEBA_DROP;
5011       break;
5012
5013     case 0x0fb9:
5014       element = EL_DC_MAGIC_WALL;
5015       break;
5016
5017     case 0x0fd0:
5018       element = EL_SPACESHIP_UP;
5019       break;
5020
5021     case 0x0fd9:
5022       element = EL_SPACESHIP_DOWN;
5023       break;
5024
5025     case 0x0ff1:
5026       element = EL_SPACESHIP_LEFT;
5027       break;
5028
5029     case 0x0ff9:
5030       element = EL_SPACESHIP_RIGHT;
5031       break;
5032
5033     case 0x1057:
5034       element = EL_BUG_UP;
5035       break;
5036
5037     case 0x1060:
5038       element = EL_BUG_DOWN;
5039       break;
5040
5041     case 0x1078:
5042       element = EL_BUG_LEFT;
5043       break;
5044
5045     case 0x1080:
5046       element = EL_BUG_RIGHT;
5047       break;
5048
5049     case 0x10de:
5050       element = EL_MOLE_UP;
5051       break;
5052
5053     case 0x10e7:
5054       element = EL_MOLE_DOWN;
5055       break;
5056
5057     case 0x10ff:
5058       element = EL_MOLE_LEFT;
5059       break;
5060
5061     case 0x1107:
5062       element = EL_MOLE_RIGHT;
5063       break;
5064
5065     case 0x11c0:
5066       element = EL_ROBOT;
5067       break;
5068
5069     case 0x13f5:
5070       element = EL_YAMYAM_UP;
5071       break;
5072
5073     case 0x1425:
5074       element = EL_SWITCHGATE_OPEN;
5075       break;
5076
5077     case 0x1426:
5078       element = EL_SWITCHGATE_CLOSED;
5079       break;
5080
5081     case 0x1437:
5082       element = EL_DC_SWITCHGATE_SWITCH_UP;
5083       break;
5084
5085     case 0x143a:
5086       element = EL_TIMEGATE_CLOSED;
5087       break;
5088
5089     case 0x144c:        // conveyor belt switch (green)
5090       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5091       break;
5092
5093     case 0x144f:        // conveyor belt switch (red)
5094       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5095       break;
5096
5097     case 0x1452:        // conveyor belt switch (blue)
5098       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5099       break;
5100
5101     case 0x145b:
5102       element = EL_CONVEYOR_BELT_3_MIDDLE;
5103       break;
5104
5105     case 0x1463:
5106       element = EL_CONVEYOR_BELT_3_LEFT;
5107       break;
5108
5109     case 0x146b:
5110       element = EL_CONVEYOR_BELT_3_RIGHT;
5111       break;
5112
5113     case 0x1473:
5114       element = EL_CONVEYOR_BELT_1_MIDDLE;
5115       break;
5116
5117     case 0x147b:
5118       element = EL_CONVEYOR_BELT_1_LEFT;
5119       break;
5120
5121     case 0x1483:
5122       element = EL_CONVEYOR_BELT_1_RIGHT;
5123       break;
5124
5125     case 0x148b:
5126       element = EL_CONVEYOR_BELT_4_MIDDLE;
5127       break;
5128
5129     case 0x1493:
5130       element = EL_CONVEYOR_BELT_4_LEFT;
5131       break;
5132
5133     case 0x149b:
5134       element = EL_CONVEYOR_BELT_4_RIGHT;
5135       break;
5136
5137     case 0x14ac:
5138       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5139       break;
5140
5141     case 0x14bd:
5142       element = EL_EXPANDABLE_WALL_VERTICAL;
5143       break;
5144
5145     case 0x14c6:
5146       element = EL_EXPANDABLE_WALL_ANY;
5147       break;
5148
5149     case 0x14ce:        // growing steel wall (left/right)
5150       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5151       break;
5152
5153     case 0x14df:        // growing steel wall (up/down)
5154       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5155       break;
5156
5157     case 0x14e8:        // growing steel wall (up/down/left/right)
5158       element = EL_EXPANDABLE_STEELWALL_ANY;
5159       break;
5160
5161     case 0x14e9:
5162       element = EL_SHIELD_DEADLY;
5163       break;
5164
5165     case 0x1501:
5166       element = EL_EXTRA_TIME;
5167       break;
5168
5169     case 0x154f:
5170       element = EL_ACID;
5171       break;
5172
5173     case 0x1577:
5174       element = EL_EMPTY_SPACE;
5175       break;
5176
5177     case 0x1578:        // quicksand (empty)
5178       element = EL_QUICKSAND_FAST_EMPTY;
5179       break;
5180
5181     case 0x1579:        // slow quicksand (empty)
5182       element = EL_QUICKSAND_EMPTY;
5183       break;
5184
5185       // 0x157c - 0x158b:
5186       // EL_SAND
5187
5188       // 0x1590 - 0x159f:
5189       // EL_DC_LANDMINE
5190
5191     case 0x15a0:
5192       element = EL_EM_DYNAMITE;
5193       break;
5194
5195     case 0x15a1:        // key (red)
5196       element = EL_EM_KEY_1;
5197       break;
5198
5199     case 0x15a2:        // key (yellow)
5200       element = EL_EM_KEY_2;
5201       break;
5202
5203     case 0x15a3:        // key (blue)
5204       element = EL_EM_KEY_4;
5205       break;
5206
5207     case 0x15a4:        // key (green)
5208       element = EL_EM_KEY_3;
5209       break;
5210
5211     case 0x15a5:        // key (white)
5212       element = EL_DC_KEY_WHITE;
5213       break;
5214
5215     case 0x15a6:
5216       element = EL_WALL_SLIPPERY;
5217       break;
5218
5219     case 0x15a7:
5220       element = EL_WALL;
5221       break;
5222
5223     case 0x15a8:        // wall (not round)
5224       element = EL_WALL;
5225       break;
5226
5227     case 0x15a9:        // (blue)
5228       element = EL_CHAR_A;
5229       break;
5230
5231     case 0x15aa:        // (blue)
5232       element = EL_CHAR_B;
5233       break;
5234
5235     case 0x15ab:        // (blue)
5236       element = EL_CHAR_C;
5237       break;
5238
5239     case 0x15ac:        // (blue)
5240       element = EL_CHAR_D;
5241       break;
5242
5243     case 0x15ad:        // (blue)
5244       element = EL_CHAR_E;
5245       break;
5246
5247     case 0x15ae:        // (blue)
5248       element = EL_CHAR_F;
5249       break;
5250
5251     case 0x15af:        // (blue)
5252       element = EL_CHAR_G;
5253       break;
5254
5255     case 0x15b0:        // (blue)
5256       element = EL_CHAR_H;
5257       break;
5258
5259     case 0x15b1:        // (blue)
5260       element = EL_CHAR_I;
5261       break;
5262
5263     case 0x15b2:        // (blue)
5264       element = EL_CHAR_J;
5265       break;
5266
5267     case 0x15b3:        // (blue)
5268       element = EL_CHAR_K;
5269       break;
5270
5271     case 0x15b4:        // (blue)
5272       element = EL_CHAR_L;
5273       break;
5274
5275     case 0x15b5:        // (blue)
5276       element = EL_CHAR_M;
5277       break;
5278
5279     case 0x15b6:        // (blue)
5280       element = EL_CHAR_N;
5281       break;
5282
5283     case 0x15b7:        // (blue)
5284       element = EL_CHAR_O;
5285       break;
5286
5287     case 0x15b8:        // (blue)
5288       element = EL_CHAR_P;
5289       break;
5290
5291     case 0x15b9:        // (blue)
5292       element = EL_CHAR_Q;
5293       break;
5294
5295     case 0x15ba:        // (blue)
5296       element = EL_CHAR_R;
5297       break;
5298
5299     case 0x15bb:        // (blue)
5300       element = EL_CHAR_S;
5301       break;
5302
5303     case 0x15bc:        // (blue)
5304       element = EL_CHAR_T;
5305       break;
5306
5307     case 0x15bd:        // (blue)
5308       element = EL_CHAR_U;
5309       break;
5310
5311     case 0x15be:        // (blue)
5312       element = EL_CHAR_V;
5313       break;
5314
5315     case 0x15bf:        // (blue)
5316       element = EL_CHAR_W;
5317       break;
5318
5319     case 0x15c0:        // (blue)
5320       element = EL_CHAR_X;
5321       break;
5322
5323     case 0x15c1:        // (blue)
5324       element = EL_CHAR_Y;
5325       break;
5326
5327     case 0x15c2:        // (blue)
5328       element = EL_CHAR_Z;
5329       break;
5330
5331     case 0x15c3:        // (blue)
5332       element = EL_CHAR_AUMLAUT;
5333       break;
5334
5335     case 0x15c4:        // (blue)
5336       element = EL_CHAR_OUMLAUT;
5337       break;
5338
5339     case 0x15c5:        // (blue)
5340       element = EL_CHAR_UUMLAUT;
5341       break;
5342
5343     case 0x15c6:        // (blue)
5344       element = EL_CHAR_0;
5345       break;
5346
5347     case 0x15c7:        // (blue)
5348       element = EL_CHAR_1;
5349       break;
5350
5351     case 0x15c8:        // (blue)
5352       element = EL_CHAR_2;
5353       break;
5354
5355     case 0x15c9:        // (blue)
5356       element = EL_CHAR_3;
5357       break;
5358
5359     case 0x15ca:        // (blue)
5360       element = EL_CHAR_4;
5361       break;
5362
5363     case 0x15cb:        // (blue)
5364       element = EL_CHAR_5;
5365       break;
5366
5367     case 0x15cc:        // (blue)
5368       element = EL_CHAR_6;
5369       break;
5370
5371     case 0x15cd:        // (blue)
5372       element = EL_CHAR_7;
5373       break;
5374
5375     case 0x15ce:        // (blue)
5376       element = EL_CHAR_8;
5377       break;
5378
5379     case 0x15cf:        // (blue)
5380       element = EL_CHAR_9;
5381       break;
5382
5383     case 0x15d0:        // (blue)
5384       element = EL_CHAR_PERIOD;
5385       break;
5386
5387     case 0x15d1:        // (blue)
5388       element = EL_CHAR_EXCLAM;
5389       break;
5390
5391     case 0x15d2:        // (blue)
5392       element = EL_CHAR_COLON;
5393       break;
5394
5395     case 0x15d3:        // (blue)
5396       element = EL_CHAR_LESS;
5397       break;
5398
5399     case 0x15d4:        // (blue)
5400       element = EL_CHAR_GREATER;
5401       break;
5402
5403     case 0x15d5:        // (blue)
5404       element = EL_CHAR_QUESTION;
5405       break;
5406
5407     case 0x15d6:        // (blue)
5408       element = EL_CHAR_COPYRIGHT;
5409       break;
5410
5411     case 0x15d7:        // (blue)
5412       element = EL_CHAR_UP;
5413       break;
5414
5415     case 0x15d8:        // (blue)
5416       element = EL_CHAR_DOWN;
5417       break;
5418
5419     case 0x15d9:        // (blue)
5420       element = EL_CHAR_BUTTON;
5421       break;
5422
5423     case 0x15da:        // (blue)
5424       element = EL_CHAR_PLUS;
5425       break;
5426
5427     case 0x15db:        // (blue)
5428       element = EL_CHAR_MINUS;
5429       break;
5430
5431     case 0x15dc:        // (blue)
5432       element = EL_CHAR_APOSTROPHE;
5433       break;
5434
5435     case 0x15dd:        // (blue)
5436       element = EL_CHAR_PARENLEFT;
5437       break;
5438
5439     case 0x15de:        // (blue)
5440       element = EL_CHAR_PARENRIGHT;
5441       break;
5442
5443     case 0x15df:        // (green)
5444       element = EL_CHAR_A;
5445       break;
5446
5447     case 0x15e0:        // (green)
5448       element = EL_CHAR_B;
5449       break;
5450
5451     case 0x15e1:        // (green)
5452       element = EL_CHAR_C;
5453       break;
5454
5455     case 0x15e2:        // (green)
5456       element = EL_CHAR_D;
5457       break;
5458
5459     case 0x15e3:        // (green)
5460       element = EL_CHAR_E;
5461       break;
5462
5463     case 0x15e4:        // (green)
5464       element = EL_CHAR_F;
5465       break;
5466
5467     case 0x15e5:        // (green)
5468       element = EL_CHAR_G;
5469       break;
5470
5471     case 0x15e6:        // (green)
5472       element = EL_CHAR_H;
5473       break;
5474
5475     case 0x15e7:        // (green)
5476       element = EL_CHAR_I;
5477       break;
5478
5479     case 0x15e8:        // (green)
5480       element = EL_CHAR_J;
5481       break;
5482
5483     case 0x15e9:        // (green)
5484       element = EL_CHAR_K;
5485       break;
5486
5487     case 0x15ea:        // (green)
5488       element = EL_CHAR_L;
5489       break;
5490
5491     case 0x15eb:        // (green)
5492       element = EL_CHAR_M;
5493       break;
5494
5495     case 0x15ec:        // (green)
5496       element = EL_CHAR_N;
5497       break;
5498
5499     case 0x15ed:        // (green)
5500       element = EL_CHAR_O;
5501       break;
5502
5503     case 0x15ee:        // (green)
5504       element = EL_CHAR_P;
5505       break;
5506
5507     case 0x15ef:        // (green)
5508       element = EL_CHAR_Q;
5509       break;
5510
5511     case 0x15f0:        // (green)
5512       element = EL_CHAR_R;
5513       break;
5514
5515     case 0x15f1:        // (green)
5516       element = EL_CHAR_S;
5517       break;
5518
5519     case 0x15f2:        // (green)
5520       element = EL_CHAR_T;
5521       break;
5522
5523     case 0x15f3:        // (green)
5524       element = EL_CHAR_U;
5525       break;
5526
5527     case 0x15f4:        // (green)
5528       element = EL_CHAR_V;
5529       break;
5530
5531     case 0x15f5:        // (green)
5532       element = EL_CHAR_W;
5533       break;
5534
5535     case 0x15f6:        // (green)
5536       element = EL_CHAR_X;
5537       break;
5538
5539     case 0x15f7:        // (green)
5540       element = EL_CHAR_Y;
5541       break;
5542
5543     case 0x15f8:        // (green)
5544       element = EL_CHAR_Z;
5545       break;
5546
5547     case 0x15f9:        // (green)
5548       element = EL_CHAR_AUMLAUT;
5549       break;
5550
5551     case 0x15fa:        // (green)
5552       element = EL_CHAR_OUMLAUT;
5553       break;
5554
5555     case 0x15fb:        // (green)
5556       element = EL_CHAR_UUMLAUT;
5557       break;
5558
5559     case 0x15fc:        // (green)
5560       element = EL_CHAR_0;
5561       break;
5562
5563     case 0x15fd:        // (green)
5564       element = EL_CHAR_1;
5565       break;
5566
5567     case 0x15fe:        // (green)
5568       element = EL_CHAR_2;
5569       break;
5570
5571     case 0x15ff:        // (green)
5572       element = EL_CHAR_3;
5573       break;
5574
5575     case 0x1600:        // (green)
5576       element = EL_CHAR_4;
5577       break;
5578
5579     case 0x1601:        // (green)
5580       element = EL_CHAR_5;
5581       break;
5582
5583     case 0x1602:        // (green)
5584       element = EL_CHAR_6;
5585       break;
5586
5587     case 0x1603:        // (green)
5588       element = EL_CHAR_7;
5589       break;
5590
5591     case 0x1604:        // (green)
5592       element = EL_CHAR_8;
5593       break;
5594
5595     case 0x1605:        // (green)
5596       element = EL_CHAR_9;
5597       break;
5598
5599     case 0x1606:        // (green)
5600       element = EL_CHAR_PERIOD;
5601       break;
5602
5603     case 0x1607:        // (green)
5604       element = EL_CHAR_EXCLAM;
5605       break;
5606
5607     case 0x1608:        // (green)
5608       element = EL_CHAR_COLON;
5609       break;
5610
5611     case 0x1609:        // (green)
5612       element = EL_CHAR_LESS;
5613       break;
5614
5615     case 0x160a:        // (green)
5616       element = EL_CHAR_GREATER;
5617       break;
5618
5619     case 0x160b:        // (green)
5620       element = EL_CHAR_QUESTION;
5621       break;
5622
5623     case 0x160c:        // (green)
5624       element = EL_CHAR_COPYRIGHT;
5625       break;
5626
5627     case 0x160d:        // (green)
5628       element = EL_CHAR_UP;
5629       break;
5630
5631     case 0x160e:        // (green)
5632       element = EL_CHAR_DOWN;
5633       break;
5634
5635     case 0x160f:        // (green)
5636       element = EL_CHAR_BUTTON;
5637       break;
5638
5639     case 0x1610:        // (green)
5640       element = EL_CHAR_PLUS;
5641       break;
5642
5643     case 0x1611:        // (green)
5644       element = EL_CHAR_MINUS;
5645       break;
5646
5647     case 0x1612:        // (green)
5648       element = EL_CHAR_APOSTROPHE;
5649       break;
5650
5651     case 0x1613:        // (green)
5652       element = EL_CHAR_PARENLEFT;
5653       break;
5654
5655     case 0x1614:        // (green)
5656       element = EL_CHAR_PARENRIGHT;
5657       break;
5658
5659     case 0x1615:        // (blue steel)
5660       element = EL_STEEL_CHAR_A;
5661       break;
5662
5663     case 0x1616:        // (blue steel)
5664       element = EL_STEEL_CHAR_B;
5665       break;
5666
5667     case 0x1617:        // (blue steel)
5668       element = EL_STEEL_CHAR_C;
5669       break;
5670
5671     case 0x1618:        // (blue steel)
5672       element = EL_STEEL_CHAR_D;
5673       break;
5674
5675     case 0x1619:        // (blue steel)
5676       element = EL_STEEL_CHAR_E;
5677       break;
5678
5679     case 0x161a:        // (blue steel)
5680       element = EL_STEEL_CHAR_F;
5681       break;
5682
5683     case 0x161b:        // (blue steel)
5684       element = EL_STEEL_CHAR_G;
5685       break;
5686
5687     case 0x161c:        // (blue steel)
5688       element = EL_STEEL_CHAR_H;
5689       break;
5690
5691     case 0x161d:        // (blue steel)
5692       element = EL_STEEL_CHAR_I;
5693       break;
5694
5695     case 0x161e:        // (blue steel)
5696       element = EL_STEEL_CHAR_J;
5697       break;
5698
5699     case 0x161f:        // (blue steel)
5700       element = EL_STEEL_CHAR_K;
5701       break;
5702
5703     case 0x1620:        // (blue steel)
5704       element = EL_STEEL_CHAR_L;
5705       break;
5706
5707     case 0x1621:        // (blue steel)
5708       element = EL_STEEL_CHAR_M;
5709       break;
5710
5711     case 0x1622:        // (blue steel)
5712       element = EL_STEEL_CHAR_N;
5713       break;
5714
5715     case 0x1623:        // (blue steel)
5716       element = EL_STEEL_CHAR_O;
5717       break;
5718
5719     case 0x1624:        // (blue steel)
5720       element = EL_STEEL_CHAR_P;
5721       break;
5722
5723     case 0x1625:        // (blue steel)
5724       element = EL_STEEL_CHAR_Q;
5725       break;
5726
5727     case 0x1626:        // (blue steel)
5728       element = EL_STEEL_CHAR_R;
5729       break;
5730
5731     case 0x1627:        // (blue steel)
5732       element = EL_STEEL_CHAR_S;
5733       break;
5734
5735     case 0x1628:        // (blue steel)
5736       element = EL_STEEL_CHAR_T;
5737       break;
5738
5739     case 0x1629:        // (blue steel)
5740       element = EL_STEEL_CHAR_U;
5741       break;
5742
5743     case 0x162a:        // (blue steel)
5744       element = EL_STEEL_CHAR_V;
5745       break;
5746
5747     case 0x162b:        // (blue steel)
5748       element = EL_STEEL_CHAR_W;
5749       break;
5750
5751     case 0x162c:        // (blue steel)
5752       element = EL_STEEL_CHAR_X;
5753       break;
5754
5755     case 0x162d:        // (blue steel)
5756       element = EL_STEEL_CHAR_Y;
5757       break;
5758
5759     case 0x162e:        // (blue steel)
5760       element = EL_STEEL_CHAR_Z;
5761       break;
5762
5763     case 0x162f:        // (blue steel)
5764       element = EL_STEEL_CHAR_AUMLAUT;
5765       break;
5766
5767     case 0x1630:        // (blue steel)
5768       element = EL_STEEL_CHAR_OUMLAUT;
5769       break;
5770
5771     case 0x1631:        // (blue steel)
5772       element = EL_STEEL_CHAR_UUMLAUT;
5773       break;
5774
5775     case 0x1632:        // (blue steel)
5776       element = EL_STEEL_CHAR_0;
5777       break;
5778
5779     case 0x1633:        // (blue steel)
5780       element = EL_STEEL_CHAR_1;
5781       break;
5782
5783     case 0x1634:        // (blue steel)
5784       element = EL_STEEL_CHAR_2;
5785       break;
5786
5787     case 0x1635:        // (blue steel)
5788       element = EL_STEEL_CHAR_3;
5789       break;
5790
5791     case 0x1636:        // (blue steel)
5792       element = EL_STEEL_CHAR_4;
5793       break;
5794
5795     case 0x1637:        // (blue steel)
5796       element = EL_STEEL_CHAR_5;
5797       break;
5798
5799     case 0x1638:        // (blue steel)
5800       element = EL_STEEL_CHAR_6;
5801       break;
5802
5803     case 0x1639:        // (blue steel)
5804       element = EL_STEEL_CHAR_7;
5805       break;
5806
5807     case 0x163a:        // (blue steel)
5808       element = EL_STEEL_CHAR_8;
5809       break;
5810
5811     case 0x163b:        // (blue steel)
5812       element = EL_STEEL_CHAR_9;
5813       break;
5814
5815     case 0x163c:        // (blue steel)
5816       element = EL_STEEL_CHAR_PERIOD;
5817       break;
5818
5819     case 0x163d:        // (blue steel)
5820       element = EL_STEEL_CHAR_EXCLAM;
5821       break;
5822
5823     case 0x163e:        // (blue steel)
5824       element = EL_STEEL_CHAR_COLON;
5825       break;
5826
5827     case 0x163f:        // (blue steel)
5828       element = EL_STEEL_CHAR_LESS;
5829       break;
5830
5831     case 0x1640:        // (blue steel)
5832       element = EL_STEEL_CHAR_GREATER;
5833       break;
5834
5835     case 0x1641:        // (blue steel)
5836       element = EL_STEEL_CHAR_QUESTION;
5837       break;
5838
5839     case 0x1642:        // (blue steel)
5840       element = EL_STEEL_CHAR_COPYRIGHT;
5841       break;
5842
5843     case 0x1643:        // (blue steel)
5844       element = EL_STEEL_CHAR_UP;
5845       break;
5846
5847     case 0x1644:        // (blue steel)
5848       element = EL_STEEL_CHAR_DOWN;
5849       break;
5850
5851     case 0x1645:        // (blue steel)
5852       element = EL_STEEL_CHAR_BUTTON;
5853       break;
5854
5855     case 0x1646:        // (blue steel)
5856       element = EL_STEEL_CHAR_PLUS;
5857       break;
5858
5859     case 0x1647:        // (blue steel)
5860       element = EL_STEEL_CHAR_MINUS;
5861       break;
5862
5863     case 0x1648:        // (blue steel)
5864       element = EL_STEEL_CHAR_APOSTROPHE;
5865       break;
5866
5867     case 0x1649:        // (blue steel)
5868       element = EL_STEEL_CHAR_PARENLEFT;
5869       break;
5870
5871     case 0x164a:        // (blue steel)
5872       element = EL_STEEL_CHAR_PARENRIGHT;
5873       break;
5874
5875     case 0x164b:        // (green steel)
5876       element = EL_STEEL_CHAR_A;
5877       break;
5878
5879     case 0x164c:        // (green steel)
5880       element = EL_STEEL_CHAR_B;
5881       break;
5882
5883     case 0x164d:        // (green steel)
5884       element = EL_STEEL_CHAR_C;
5885       break;
5886
5887     case 0x164e:        // (green steel)
5888       element = EL_STEEL_CHAR_D;
5889       break;
5890
5891     case 0x164f:        // (green steel)
5892       element = EL_STEEL_CHAR_E;
5893       break;
5894
5895     case 0x1650:        // (green steel)
5896       element = EL_STEEL_CHAR_F;
5897       break;
5898
5899     case 0x1651:        // (green steel)
5900       element = EL_STEEL_CHAR_G;
5901       break;
5902
5903     case 0x1652:        // (green steel)
5904       element = EL_STEEL_CHAR_H;
5905       break;
5906
5907     case 0x1653:        // (green steel)
5908       element = EL_STEEL_CHAR_I;
5909       break;
5910
5911     case 0x1654:        // (green steel)
5912       element = EL_STEEL_CHAR_J;
5913       break;
5914
5915     case 0x1655:        // (green steel)
5916       element = EL_STEEL_CHAR_K;
5917       break;
5918
5919     case 0x1656:        // (green steel)
5920       element = EL_STEEL_CHAR_L;
5921       break;
5922
5923     case 0x1657:        // (green steel)
5924       element = EL_STEEL_CHAR_M;
5925       break;
5926
5927     case 0x1658:        // (green steel)
5928       element = EL_STEEL_CHAR_N;
5929       break;
5930
5931     case 0x1659:        // (green steel)
5932       element = EL_STEEL_CHAR_O;
5933       break;
5934
5935     case 0x165a:        // (green steel)
5936       element = EL_STEEL_CHAR_P;
5937       break;
5938
5939     case 0x165b:        // (green steel)
5940       element = EL_STEEL_CHAR_Q;
5941       break;
5942
5943     case 0x165c:        // (green steel)
5944       element = EL_STEEL_CHAR_R;
5945       break;
5946
5947     case 0x165d:        // (green steel)
5948       element = EL_STEEL_CHAR_S;
5949       break;
5950
5951     case 0x165e:        // (green steel)
5952       element = EL_STEEL_CHAR_T;
5953       break;
5954
5955     case 0x165f:        // (green steel)
5956       element = EL_STEEL_CHAR_U;
5957       break;
5958
5959     case 0x1660:        // (green steel)
5960       element = EL_STEEL_CHAR_V;
5961       break;
5962
5963     case 0x1661:        // (green steel)
5964       element = EL_STEEL_CHAR_W;
5965       break;
5966
5967     case 0x1662:        // (green steel)
5968       element = EL_STEEL_CHAR_X;
5969       break;
5970
5971     case 0x1663:        // (green steel)
5972       element = EL_STEEL_CHAR_Y;
5973       break;
5974
5975     case 0x1664:        // (green steel)
5976       element = EL_STEEL_CHAR_Z;
5977       break;
5978
5979     case 0x1665:        // (green steel)
5980       element = EL_STEEL_CHAR_AUMLAUT;
5981       break;
5982
5983     case 0x1666:        // (green steel)
5984       element = EL_STEEL_CHAR_OUMLAUT;
5985       break;
5986
5987     case 0x1667:        // (green steel)
5988       element = EL_STEEL_CHAR_UUMLAUT;
5989       break;
5990
5991     case 0x1668:        // (green steel)
5992       element = EL_STEEL_CHAR_0;
5993       break;
5994
5995     case 0x1669:        // (green steel)
5996       element = EL_STEEL_CHAR_1;
5997       break;
5998
5999     case 0x166a:        // (green steel)
6000       element = EL_STEEL_CHAR_2;
6001       break;
6002
6003     case 0x166b:        // (green steel)
6004       element = EL_STEEL_CHAR_3;
6005       break;
6006
6007     case 0x166c:        // (green steel)
6008       element = EL_STEEL_CHAR_4;
6009       break;
6010
6011     case 0x166d:        // (green steel)
6012       element = EL_STEEL_CHAR_5;
6013       break;
6014
6015     case 0x166e:        // (green steel)
6016       element = EL_STEEL_CHAR_6;
6017       break;
6018
6019     case 0x166f:        // (green steel)
6020       element = EL_STEEL_CHAR_7;
6021       break;
6022
6023     case 0x1670:        // (green steel)
6024       element = EL_STEEL_CHAR_8;
6025       break;
6026
6027     case 0x1671:        // (green steel)
6028       element = EL_STEEL_CHAR_9;
6029       break;
6030
6031     case 0x1672:        // (green steel)
6032       element = EL_STEEL_CHAR_PERIOD;
6033       break;
6034
6035     case 0x1673:        // (green steel)
6036       element = EL_STEEL_CHAR_EXCLAM;
6037       break;
6038
6039     case 0x1674:        // (green steel)
6040       element = EL_STEEL_CHAR_COLON;
6041       break;
6042
6043     case 0x1675:        // (green steel)
6044       element = EL_STEEL_CHAR_LESS;
6045       break;
6046
6047     case 0x1676:        // (green steel)
6048       element = EL_STEEL_CHAR_GREATER;
6049       break;
6050
6051     case 0x1677:        // (green steel)
6052       element = EL_STEEL_CHAR_QUESTION;
6053       break;
6054
6055     case 0x1678:        // (green steel)
6056       element = EL_STEEL_CHAR_COPYRIGHT;
6057       break;
6058
6059     case 0x1679:        // (green steel)
6060       element = EL_STEEL_CHAR_UP;
6061       break;
6062
6063     case 0x167a:        // (green steel)
6064       element = EL_STEEL_CHAR_DOWN;
6065       break;
6066
6067     case 0x167b:        // (green steel)
6068       element = EL_STEEL_CHAR_BUTTON;
6069       break;
6070
6071     case 0x167c:        // (green steel)
6072       element = EL_STEEL_CHAR_PLUS;
6073       break;
6074
6075     case 0x167d:        // (green steel)
6076       element = EL_STEEL_CHAR_MINUS;
6077       break;
6078
6079     case 0x167e:        // (green steel)
6080       element = EL_STEEL_CHAR_APOSTROPHE;
6081       break;
6082
6083     case 0x167f:        // (green steel)
6084       element = EL_STEEL_CHAR_PARENLEFT;
6085       break;
6086
6087     case 0x1680:        // (green steel)
6088       element = EL_STEEL_CHAR_PARENRIGHT;
6089       break;
6090
6091     case 0x1681:        // gate (red)
6092       element = EL_EM_GATE_1;
6093       break;
6094
6095     case 0x1682:        // secret gate (red)
6096       element = EL_EM_GATE_1_GRAY;
6097       break;
6098
6099     case 0x1683:        // gate (yellow)
6100       element = EL_EM_GATE_2;
6101       break;
6102
6103     case 0x1684:        // secret gate (yellow)
6104       element = EL_EM_GATE_2_GRAY;
6105       break;
6106
6107     case 0x1685:        // gate (blue)
6108       element = EL_EM_GATE_4;
6109       break;
6110
6111     case 0x1686:        // secret gate (blue)
6112       element = EL_EM_GATE_4_GRAY;
6113       break;
6114
6115     case 0x1687:        // gate (green)
6116       element = EL_EM_GATE_3;
6117       break;
6118
6119     case 0x1688:        // secret gate (green)
6120       element = EL_EM_GATE_3_GRAY;
6121       break;
6122
6123     case 0x1689:        // gate (white)
6124       element = EL_DC_GATE_WHITE;
6125       break;
6126
6127     case 0x168a:        // secret gate (white)
6128       element = EL_DC_GATE_WHITE_GRAY;
6129       break;
6130
6131     case 0x168b:        // secret gate (no key)
6132       element = EL_DC_GATE_FAKE_GRAY;
6133       break;
6134
6135     case 0x168c:
6136       element = EL_ROBOT_WHEEL;
6137       break;
6138
6139     case 0x168d:
6140       element = EL_DC_TIMEGATE_SWITCH;
6141       break;
6142
6143     case 0x168e:
6144       element = EL_ACID_POOL_BOTTOM;
6145       break;
6146
6147     case 0x168f:
6148       element = EL_ACID_POOL_TOPLEFT;
6149       break;
6150
6151     case 0x1690:
6152       element = EL_ACID_POOL_TOPRIGHT;
6153       break;
6154
6155     case 0x1691:
6156       element = EL_ACID_POOL_BOTTOMLEFT;
6157       break;
6158
6159     case 0x1692:
6160       element = EL_ACID_POOL_BOTTOMRIGHT;
6161       break;
6162
6163     case 0x1693:
6164       element = EL_STEELWALL;
6165       break;
6166
6167     case 0x1694:
6168       element = EL_STEELWALL_SLIPPERY;
6169       break;
6170
6171     case 0x1695:        // steel wall (not round)
6172       element = EL_STEELWALL;
6173       break;
6174
6175     case 0x1696:        // steel wall (left)
6176       element = EL_DC_STEELWALL_1_LEFT;
6177       break;
6178
6179     case 0x1697:        // steel wall (bottom)
6180       element = EL_DC_STEELWALL_1_BOTTOM;
6181       break;
6182
6183     case 0x1698:        // steel wall (right)
6184       element = EL_DC_STEELWALL_1_RIGHT;
6185       break;
6186
6187     case 0x1699:        // steel wall (top)
6188       element = EL_DC_STEELWALL_1_TOP;
6189       break;
6190
6191     case 0x169a:        // steel wall (left/bottom)
6192       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6193       break;
6194
6195     case 0x169b:        // steel wall (right/bottom)
6196       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6197       break;
6198
6199     case 0x169c:        // steel wall (right/top)
6200       element = EL_DC_STEELWALL_1_TOPRIGHT;
6201       break;
6202
6203     case 0x169d:        // steel wall (left/top)
6204       element = EL_DC_STEELWALL_1_TOPLEFT;
6205       break;
6206
6207     case 0x169e:        // steel wall (right/bottom small)
6208       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6209       break;
6210
6211     case 0x169f:        // steel wall (left/bottom small)
6212       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6213       break;
6214
6215     case 0x16a0:        // steel wall (right/top small)
6216       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6217       break;
6218
6219     case 0x16a1:        // steel wall (left/top small)
6220       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6221       break;
6222
6223     case 0x16a2:        // steel wall (left/right)
6224       element = EL_DC_STEELWALL_1_VERTICAL;
6225       break;
6226
6227     case 0x16a3:        // steel wall (top/bottom)
6228       element = EL_DC_STEELWALL_1_HORIZONTAL;
6229       break;
6230
6231     case 0x16a4:        // steel wall 2 (left end)
6232       element = EL_DC_STEELWALL_2_LEFT;
6233       break;
6234
6235     case 0x16a5:        // steel wall 2 (right end)
6236       element = EL_DC_STEELWALL_2_RIGHT;
6237       break;
6238
6239     case 0x16a6:        // steel wall 2 (top end)
6240       element = EL_DC_STEELWALL_2_TOP;
6241       break;
6242
6243     case 0x16a7:        // steel wall 2 (bottom end)
6244       element = EL_DC_STEELWALL_2_BOTTOM;
6245       break;
6246
6247     case 0x16a8:        // steel wall 2 (left/right)
6248       element = EL_DC_STEELWALL_2_HORIZONTAL;
6249       break;
6250
6251     case 0x16a9:        // steel wall 2 (up/down)
6252       element = EL_DC_STEELWALL_2_VERTICAL;
6253       break;
6254
6255     case 0x16aa:        // steel wall 2 (mid)
6256       element = EL_DC_STEELWALL_2_MIDDLE;
6257       break;
6258
6259     case 0x16ab:
6260       element = EL_SIGN_EXCLAMATION;
6261       break;
6262
6263     case 0x16ac:
6264       element = EL_SIGN_RADIOACTIVITY;
6265       break;
6266
6267     case 0x16ad:
6268       element = EL_SIGN_STOP;
6269       break;
6270
6271     case 0x16ae:
6272       element = EL_SIGN_WHEELCHAIR;
6273       break;
6274
6275     case 0x16af:
6276       element = EL_SIGN_PARKING;
6277       break;
6278
6279     case 0x16b0:
6280       element = EL_SIGN_NO_ENTRY;
6281       break;
6282
6283     case 0x16b1:
6284       element = EL_SIGN_HEART;
6285       break;
6286
6287     case 0x16b2:
6288       element = EL_SIGN_GIVE_WAY;
6289       break;
6290
6291     case 0x16b3:
6292       element = EL_SIGN_ENTRY_FORBIDDEN;
6293       break;
6294
6295     case 0x16b4:
6296       element = EL_SIGN_EMERGENCY_EXIT;
6297       break;
6298
6299     case 0x16b5:
6300       element = EL_SIGN_YIN_YANG;
6301       break;
6302
6303     case 0x16b6:
6304       element = EL_WALL_EMERALD;
6305       break;
6306
6307     case 0x16b7:
6308       element = EL_WALL_DIAMOND;
6309       break;
6310
6311     case 0x16b8:
6312       element = EL_WALL_PEARL;
6313       break;
6314
6315     case 0x16b9:
6316       element = EL_WALL_CRYSTAL;
6317       break;
6318
6319     case 0x16ba:
6320       element = EL_INVISIBLE_WALL;
6321       break;
6322
6323     case 0x16bb:
6324       element = EL_INVISIBLE_STEELWALL;
6325       break;
6326
6327       // 0x16bc - 0x16cb:
6328       // EL_INVISIBLE_SAND
6329
6330     case 0x16cc:
6331       element = EL_LIGHT_SWITCH;
6332       break;
6333
6334     case 0x16cd:
6335       element = EL_ENVELOPE_1;
6336       break;
6337
6338     default:
6339       if (element >= 0x0117 && element <= 0x036e)       // (?)
6340         element = EL_DIAMOND;
6341       else if (element >= 0x042d && element <= 0x0684)  // (?)
6342         element = EL_EMERALD;
6343       else if (element >= 0x157c && element <= 0x158b)
6344         element = EL_SAND;
6345       else if (element >= 0x1590 && element <= 0x159f)
6346         element = EL_DC_LANDMINE;
6347       else if (element >= 0x16bc && element <= 0x16cb)
6348         element = EL_INVISIBLE_SAND;
6349       else
6350       {
6351         Warn("unknown Diamond Caves element 0x%04x", element);
6352
6353         element = EL_UNKNOWN;
6354       }
6355       break;
6356   }
6357
6358   return getMappedElement(element);
6359 }
6360
6361 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6362 {
6363   byte header[DC_LEVEL_HEADER_SIZE];
6364   int envelope_size;
6365   int envelope_header_pos = 62;
6366   int envelope_content_pos = 94;
6367   int level_name_pos = 251;
6368   int level_author_pos = 292;
6369   int envelope_header_len;
6370   int envelope_content_len;
6371   int level_name_len;
6372   int level_author_len;
6373   int fieldx, fieldy;
6374   int num_yamyam_contents;
6375   int i, x, y;
6376
6377   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6378
6379   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6380   {
6381     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6382
6383     header[i * 2 + 0] = header_word >> 8;
6384     header[i * 2 + 1] = header_word & 0xff;
6385   }
6386
6387   // read some values from level header to check level decoding integrity
6388   fieldx = header[6] | (header[7] << 8);
6389   fieldy = header[8] | (header[9] << 8);
6390   num_yamyam_contents = header[60] | (header[61] << 8);
6391
6392   // do some simple sanity checks to ensure that level was correctly decoded
6393   if (fieldx < 1 || fieldx > 256 ||
6394       fieldy < 1 || fieldy > 256 ||
6395       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6396   {
6397     level->no_valid_file = TRUE;
6398
6399     Warn("cannot decode level from stream -- using empty level");
6400
6401     return;
6402   }
6403
6404   // maximum envelope header size is 31 bytes
6405   envelope_header_len   = header[envelope_header_pos];
6406   // maximum envelope content size is 110 (156?) bytes
6407   envelope_content_len  = header[envelope_content_pos];
6408
6409   // maximum level title size is 40 bytes
6410   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6411   // maximum level author size is 30 (51?) bytes
6412   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6413
6414   envelope_size = 0;
6415
6416   for (i = 0; i < envelope_header_len; i++)
6417     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6418       level->envelope[0].text[envelope_size++] =
6419         header[envelope_header_pos + 1 + i];
6420
6421   if (envelope_header_len > 0 && envelope_content_len > 0)
6422   {
6423     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6424       level->envelope[0].text[envelope_size++] = '\n';
6425     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6426       level->envelope[0].text[envelope_size++] = '\n';
6427   }
6428
6429   for (i = 0; i < envelope_content_len; i++)
6430     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6431       level->envelope[0].text[envelope_size++] =
6432         header[envelope_content_pos + 1 + i];
6433
6434   level->envelope[0].text[envelope_size] = '\0';
6435
6436   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6437   level->envelope[0].ysize = 10;
6438   level->envelope[0].autowrap = TRUE;
6439   level->envelope[0].centered = TRUE;
6440
6441   for (i = 0; i < level_name_len; i++)
6442     level->name[i] = header[level_name_pos + 1 + i];
6443   level->name[level_name_len] = '\0';
6444
6445   for (i = 0; i < level_author_len; i++)
6446     level->author[i] = header[level_author_pos + 1 + i];
6447   level->author[level_author_len] = '\0';
6448
6449   num_yamyam_contents = header[60] | (header[61] << 8);
6450   level->num_yamyam_contents =
6451     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6452
6453   for (i = 0; i < num_yamyam_contents; i++)
6454   {
6455     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6456     {
6457       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6458       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6459
6460       if (i < MAX_ELEMENT_CONTENTS)
6461         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6462     }
6463   }
6464
6465   fieldx = header[6] | (header[7] << 8);
6466   fieldy = header[8] | (header[9] << 8);
6467   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6468   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6469
6470   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6471   {
6472     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6473     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6474
6475     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6476       level->field[x][y] = getMappedElement_DC(element_dc);
6477   }
6478
6479   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6480   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6481   level->field[x][y] = EL_PLAYER_1;
6482
6483   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6484   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6485   level->field[x][y] = EL_PLAYER_2;
6486
6487   level->gems_needed            = header[18] | (header[19] << 8);
6488
6489   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6490   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6491   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6492   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6493   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6494   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6495   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6496   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6497   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6498   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6499   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6500   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6501
6502   level->time                   = header[44] | (header[45] << 8);
6503
6504   level->amoeba_speed           = header[46] | (header[47] << 8);
6505   level->time_light             = header[48] | (header[49] << 8);
6506   level->time_timegate          = header[50] | (header[51] << 8);
6507   level->time_wheel             = header[52] | (header[53] << 8);
6508   level->time_magic_wall        = header[54] | (header[55] << 8);
6509   level->extra_time             = header[56] | (header[57] << 8);
6510   level->shield_normal_time     = header[58] | (header[59] << 8);
6511
6512   // shield and extra time elements do not have a score
6513   level->score[SC_SHIELD]       = 0;
6514   level->extra_time_score       = 0;
6515
6516   // set time for normal and deadly shields to the same value
6517   level->shield_deadly_time     = level->shield_normal_time;
6518
6519   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6520   // can slip down from flat walls, like normal walls and steel walls
6521   level->em_slippery_gems = TRUE;
6522
6523   // time score is counted for each 10 seconds left in Diamond Caves levels
6524   level->time_score_base = 10;
6525 }
6526
6527 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6528                                      struct LevelFileInfo *level_file_info,
6529                                      boolean level_info_only)
6530 {
6531   char *filename = level_file_info->filename;
6532   File *file;
6533   int num_magic_bytes = 8;
6534   char magic_bytes[num_magic_bytes + 1];
6535   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6536
6537   if (!(file = openFile(filename, MODE_READ)))
6538   {
6539     level->no_valid_file = TRUE;
6540
6541     if (!level_info_only)
6542       Warn("cannot read level '%s' -- using empty level", filename);
6543
6544     return;
6545   }
6546
6547   // fseek(file, 0x0000, SEEK_SET);
6548
6549   if (level_file_info->packed)
6550   {
6551     // read "magic bytes" from start of file
6552     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6553       magic_bytes[0] = '\0';
6554
6555     // check "magic bytes" for correct file format
6556     if (!strPrefix(magic_bytes, "DC2"))
6557     {
6558       level->no_valid_file = TRUE;
6559
6560       Warn("unknown DC level file '%s' -- using empty level", filename);
6561
6562       return;
6563     }
6564
6565     if (strPrefix(magic_bytes, "DC2Win95") ||
6566         strPrefix(magic_bytes, "DC2Win98"))
6567     {
6568       int position_first_level = 0x00fa;
6569       int extra_bytes = 4;
6570       int skip_bytes;
6571
6572       // advance file stream to first level inside the level package
6573       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6574
6575       // each block of level data is followed by block of non-level data
6576       num_levels_to_skip *= 2;
6577
6578       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6579       while (num_levels_to_skip >= 0)
6580       {
6581         // advance file stream to next level inside the level package
6582         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6583         {
6584           level->no_valid_file = TRUE;
6585
6586           Warn("cannot fseek in file '%s' -- using empty level", filename);
6587
6588           return;
6589         }
6590
6591         // skip apparently unused extra bytes following each level
6592         ReadUnusedBytesFromFile(file, extra_bytes);
6593
6594         // read size of next level in level package
6595         skip_bytes = getFile32BitLE(file);
6596
6597         num_levels_to_skip--;
6598       }
6599     }
6600     else
6601     {
6602       level->no_valid_file = TRUE;
6603
6604       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6605
6606       return;
6607     }
6608   }
6609
6610   LoadLevelFromFileStream_DC(file, level);
6611
6612   closeFile(file);
6613 }
6614
6615
6616 // ----------------------------------------------------------------------------
6617 // functions for loading SB level
6618 // ----------------------------------------------------------------------------
6619
6620 int getMappedElement_SB(int element_ascii, boolean use_ces)
6621 {
6622   static struct
6623   {
6624     int ascii;
6625     int sb;
6626     int ce;
6627   }
6628   sb_element_mapping[] =
6629   {
6630     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6631     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6632     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6633     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6634     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6635     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6636     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6637     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6638
6639     { 0,   -1,                      -1          },
6640   };
6641
6642   int i;
6643
6644   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6645     if (element_ascii == sb_element_mapping[i].ascii)
6646       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6647
6648   return EL_UNDEFINED;
6649 }
6650
6651 static void SetLevelSettings_SB(struct LevelInfo *level)
6652 {
6653   // time settings
6654   level->time = 0;
6655   level->use_step_counter = TRUE;
6656
6657   // score settings
6658   level->score[SC_TIME_BONUS] = 0;
6659   level->time_score_base = 1;
6660   level->rate_time_over_score = TRUE;
6661
6662   // game settings
6663   level->auto_exit_sokoban = TRUE;
6664 }
6665
6666 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6667                                      struct LevelFileInfo *level_file_info,
6668                                      boolean level_info_only)
6669 {
6670   char *filename = level_file_info->filename;
6671   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6672   char last_comment[MAX_LINE_LEN];
6673   char level_name[MAX_LINE_LEN];
6674   char *line_ptr;
6675   File *file;
6676   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6677   boolean read_continued_line = FALSE;
6678   boolean reading_playfield = FALSE;
6679   boolean got_valid_playfield_line = FALSE;
6680   boolean invalid_playfield_char = FALSE;
6681   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6682   int file_level_nr = 0;
6683   int x = 0, y = 0;             // initialized to make compilers happy
6684
6685   last_comment[0] = '\0';
6686   level_name[0] = '\0';
6687
6688   if (!(file = openFile(filename, MODE_READ)))
6689   {
6690     level->no_valid_file = TRUE;
6691
6692     if (!level_info_only)
6693       Warn("cannot read level '%s' -- using empty level", filename);
6694
6695     return;
6696   }
6697
6698   while (!checkEndOfFile(file))
6699   {
6700     // level successfully read, but next level may follow here
6701     if (!got_valid_playfield_line && reading_playfield)
6702     {
6703       // read playfield from single level file -- skip remaining file
6704       if (!level_file_info->packed)
6705         break;
6706
6707       if (file_level_nr >= num_levels_to_skip)
6708         break;
6709
6710       file_level_nr++;
6711
6712       last_comment[0] = '\0';
6713       level_name[0] = '\0';
6714
6715       reading_playfield = FALSE;
6716     }
6717
6718     got_valid_playfield_line = FALSE;
6719
6720     // read next line of input file
6721     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6722       break;
6723
6724     // cut trailing line break (this can be newline and/or carriage return)
6725     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6726       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6727         *line_ptr = '\0';
6728
6729     // copy raw input line for later use (mainly debugging output)
6730     strcpy(line_raw, line);
6731
6732     if (read_continued_line)
6733     {
6734       // append new line to existing line, if there is enough space
6735       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6736         strcat(previous_line, line_ptr);
6737
6738       strcpy(line, previous_line);      // copy storage buffer to line
6739
6740       read_continued_line = FALSE;
6741     }
6742
6743     // if the last character is '\', continue at next line
6744     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6745     {
6746       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6747       strcpy(previous_line, line);      // copy line to storage buffer
6748
6749       read_continued_line = TRUE;
6750
6751       continue;
6752     }
6753
6754     // skip empty lines
6755     if (line[0] == '\0')
6756       continue;
6757
6758     // extract comment text from comment line
6759     if (line[0] == ';')
6760     {
6761       for (line_ptr = line; *line_ptr; line_ptr++)
6762         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6763           break;
6764
6765       strcpy(last_comment, line_ptr);
6766
6767       continue;
6768     }
6769
6770     // extract level title text from line containing level title
6771     if (line[0] == '\'')
6772     {
6773       strcpy(level_name, &line[1]);
6774
6775       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6776         level_name[strlen(level_name) - 1] = '\0';
6777
6778       continue;
6779     }
6780
6781     // skip lines containing only spaces (or empty lines)
6782     for (line_ptr = line; *line_ptr; line_ptr++)
6783       if (*line_ptr != ' ')
6784         break;
6785     if (*line_ptr == '\0')
6786       continue;
6787
6788     // at this point, we have found a line containing part of a playfield
6789
6790     got_valid_playfield_line = TRUE;
6791
6792     if (!reading_playfield)
6793     {
6794       reading_playfield = TRUE;
6795       invalid_playfield_char = FALSE;
6796
6797       for (x = 0; x < MAX_LEV_FIELDX; x++)
6798         for (y = 0; y < MAX_LEV_FIELDY; y++)
6799           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6800
6801       level->fieldx = 0;
6802       level->fieldy = 0;
6803
6804       // start with topmost tile row
6805       y = 0;
6806     }
6807
6808     // skip playfield line if larger row than allowed
6809     if (y >= MAX_LEV_FIELDY)
6810       continue;
6811
6812     // start with leftmost tile column
6813     x = 0;
6814
6815     // read playfield elements from line
6816     for (line_ptr = line; *line_ptr; line_ptr++)
6817     {
6818       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6819
6820       // stop parsing playfield line if larger column than allowed
6821       if (x >= MAX_LEV_FIELDX)
6822         break;
6823
6824       if (mapped_sb_element == EL_UNDEFINED)
6825       {
6826         invalid_playfield_char = TRUE;
6827
6828         break;
6829       }
6830
6831       level->field[x][y] = mapped_sb_element;
6832
6833       // continue with next tile column
6834       x++;
6835
6836       level->fieldx = MAX(x, level->fieldx);
6837     }
6838
6839     if (invalid_playfield_char)
6840     {
6841       // if first playfield line, treat invalid lines as comment lines
6842       if (y == 0)
6843         reading_playfield = FALSE;
6844
6845       continue;
6846     }
6847
6848     // continue with next tile row
6849     y++;
6850   }
6851
6852   closeFile(file);
6853
6854   level->fieldy = y;
6855
6856   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6857   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6858
6859   if (!reading_playfield)
6860   {
6861     level->no_valid_file = TRUE;
6862
6863     Warn("cannot read level '%s' -- using empty level", filename);
6864
6865     return;
6866   }
6867
6868   if (*level_name != '\0')
6869   {
6870     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6871     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6872   }
6873   else if (*last_comment != '\0')
6874   {
6875     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6876     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6877   }
6878   else
6879   {
6880     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6881   }
6882
6883   // set all empty fields beyond the border walls to invisible steel wall
6884   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6885   {
6886     if ((x == 0 || x == level->fieldx - 1 ||
6887          y == 0 || y == level->fieldy - 1) &&
6888         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6889       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6890                      level->field, level->fieldx, level->fieldy);
6891   }
6892
6893   // set special level settings for Sokoban levels
6894   SetLevelSettings_SB(level);
6895
6896   if (load_xsb_to_ces)
6897   {
6898     // special global settings can now be set in level template
6899     level->use_custom_template = TRUE;
6900   }
6901 }
6902
6903
6904 // -------------------------------------------------------------------------
6905 // functions for handling native levels
6906 // -------------------------------------------------------------------------
6907
6908 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6909                                      struct LevelFileInfo *level_file_info,
6910                                      boolean level_info_only)
6911 {
6912   int pos = 0;
6913
6914   // determine position of requested level inside level package
6915   if (level_file_info->packed)
6916     pos = level_file_info->nr - leveldir_current->first_level;
6917
6918   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6919     level->no_valid_file = TRUE;
6920 }
6921
6922 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6923                                      struct LevelFileInfo *level_file_info,
6924                                      boolean level_info_only)
6925 {
6926   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6927     level->no_valid_file = TRUE;
6928 }
6929
6930 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6931                                      struct LevelFileInfo *level_file_info,
6932                                      boolean level_info_only)
6933 {
6934   int pos = 0;
6935
6936   // determine position of requested level inside level package
6937   if (level_file_info->packed)
6938     pos = level_file_info->nr - leveldir_current->first_level;
6939
6940   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6941     level->no_valid_file = TRUE;
6942 }
6943
6944 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6945                                      struct LevelFileInfo *level_file_info,
6946                                      boolean level_info_only)
6947 {
6948   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6949     level->no_valid_file = TRUE;
6950 }
6951
6952 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6953 {
6954   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6955     CopyNativeLevel_RND_to_BD(level);
6956   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6957     CopyNativeLevel_RND_to_EM(level);
6958   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6959     CopyNativeLevel_RND_to_SP(level);
6960   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6961     CopyNativeLevel_RND_to_MM(level);
6962 }
6963
6964 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6965 {
6966   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6967     CopyNativeLevel_BD_to_RND(level);
6968   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6969     CopyNativeLevel_EM_to_RND(level);
6970   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6971     CopyNativeLevel_SP_to_RND(level);
6972   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6973     CopyNativeLevel_MM_to_RND(level);
6974 }
6975
6976 void SaveNativeLevel(struct LevelInfo *level)
6977 {
6978   // saving native level files only supported for some game engines
6979   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6980       level->game_engine_type != GAME_ENGINE_TYPE_SP)
6981     return;
6982
6983   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6984                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6985   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6986   char *filename = getLevelFilenameFromBasename(basename);
6987
6988   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6989     return;
6990
6991   boolean success = FALSE;
6992
6993   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6994   {
6995     CopyNativeLevel_RND_to_BD(level);
6996     // CopyNativeTape_RND_to_BD(level);
6997
6998     success = SaveNativeLevel_BD(filename);
6999   }
7000   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7001   {
7002     CopyNativeLevel_RND_to_SP(level);
7003     CopyNativeTape_RND_to_SP(level);
7004
7005     success = SaveNativeLevel_SP(filename);
7006   }
7007
7008   if (success)
7009     Request("Native level file saved!", REQ_CONFIRM);
7010   else
7011     Request("Failed to save native level file!", REQ_CONFIRM);
7012 }
7013
7014
7015 // ----------------------------------------------------------------------------
7016 // functions for loading generic level
7017 // ----------------------------------------------------------------------------
7018
7019 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7020                                   struct LevelFileInfo *level_file_info,
7021                                   boolean level_info_only)
7022 {
7023   // always start with reliable default values
7024   setLevelInfoToDefaults(level, level_info_only, TRUE);
7025
7026   switch (level_file_info->type)
7027   {
7028     case LEVEL_FILE_TYPE_RND:
7029       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7030       break;
7031
7032     case LEVEL_FILE_TYPE_BD:
7033       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7034       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7035       break;
7036
7037     case LEVEL_FILE_TYPE_EM:
7038       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7039       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7040       break;
7041
7042     case LEVEL_FILE_TYPE_SP:
7043       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7044       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7045       break;
7046
7047     case LEVEL_FILE_TYPE_MM:
7048       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7049       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7050       break;
7051
7052     case LEVEL_FILE_TYPE_DC:
7053       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7054       break;
7055
7056     case LEVEL_FILE_TYPE_SB:
7057       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7058       break;
7059
7060     default:
7061       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7062       break;
7063   }
7064
7065   // if level file is invalid, restore level structure to default values
7066   if (level->no_valid_file)
7067     setLevelInfoToDefaults(level, level_info_only, FALSE);
7068
7069   if (check_special_flags("use_native_bd_game_engine"))
7070     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7071
7072   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7073     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7074
7075   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7076     CopyNativeLevel_Native_to_RND(level);
7077 }
7078
7079 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7080 {
7081   static struct LevelFileInfo level_file_info;
7082
7083   // always start with reliable default values
7084   setFileInfoToDefaults(&level_file_info);
7085
7086   level_file_info.nr = 0;                       // unknown level number
7087   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7088
7089   setString(&level_file_info.filename, filename);
7090
7091   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7092 }
7093
7094 static void LoadLevel_InitVersion(struct LevelInfo *level)
7095 {
7096   int i, j;
7097
7098   if (leveldir_current == NULL)         // only when dumping level
7099     return;
7100
7101   // all engine modifications also valid for levels which use latest engine
7102   if (level->game_version < VERSION_IDENT(3,2,0,5))
7103   {
7104     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7105     level->time_score_base = 10;
7106   }
7107
7108   if (leveldir_current->latest_engine)
7109   {
7110     // ---------- use latest game engine --------------------------------------
7111
7112     /* For all levels which are forced to use the latest game engine version
7113        (normally all but user contributed, private and undefined levels), set
7114        the game engine version to the actual version; this allows for actual
7115        corrections in the game engine to take effect for existing, converted
7116        levels (from "classic" or other existing games) to make the emulation
7117        of the corresponding game more accurate, while (hopefully) not breaking
7118        existing levels created from other players. */
7119
7120     level->game_version = GAME_VERSION_ACTUAL;
7121
7122     /* Set special EM style gems behaviour: EM style gems slip down from
7123        normal, steel and growing wall. As this is a more fundamental change,
7124        it seems better to set the default behaviour to "off" (as it is more
7125        natural) and make it configurable in the level editor (as a property
7126        of gem style elements). Already existing converted levels (neither
7127        private nor contributed levels) are changed to the new behaviour. */
7128
7129     if (level->file_version < FILE_VERSION_2_0)
7130       level->em_slippery_gems = TRUE;
7131
7132     return;
7133   }
7134
7135   // ---------- use game engine the level was created with --------------------
7136
7137   /* For all levels which are not forced to use the latest game engine
7138      version (normally user contributed, private and undefined levels),
7139      use the version of the game engine the levels were created for.
7140
7141      Since 2.0.1, the game engine version is now directly stored
7142      in the level file (chunk "VERS"), so there is no need anymore
7143      to set the game version from the file version (except for old,
7144      pre-2.0 levels, where the game version is still taken from the
7145      file format version used to store the level -- see above). */
7146
7147   // player was faster than enemies in 1.0.0 and before
7148   if (level->file_version == FILE_VERSION_1_0)
7149     for (i = 0; i < MAX_PLAYERS; i++)
7150       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7151
7152   // default behaviour for EM style gems was "slippery" only in 2.0.1
7153   if (level->game_version == VERSION_IDENT(2,0,1,0))
7154     level->em_slippery_gems = TRUE;
7155
7156   // springs could be pushed over pits before (pre-release version) 2.2.0
7157   if (level->game_version < VERSION_IDENT(2,2,0,0))
7158     level->use_spring_bug = TRUE;
7159
7160   if (level->game_version < VERSION_IDENT(3,2,0,5))
7161   {
7162     // time orb caused limited time in endless time levels before 3.2.0-5
7163     level->use_time_orb_bug = TRUE;
7164
7165     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7166     level->block_snap_field = FALSE;
7167
7168     // extra time score was same value as time left score before 3.2.0-5
7169     level->extra_time_score = level->score[SC_TIME_BONUS];
7170   }
7171
7172   if (level->game_version < VERSION_IDENT(3,2,0,7))
7173   {
7174     // default behaviour for snapping was "not continuous" before 3.2.0-7
7175     level->continuous_snapping = FALSE;
7176   }
7177
7178   // only few elements were able to actively move into acid before 3.1.0
7179   // trigger settings did not exist before 3.1.0; set to default "any"
7180   if (level->game_version < VERSION_IDENT(3,1,0,0))
7181   {
7182     // correct "can move into acid" settings (all zero in old levels)
7183
7184     level->can_move_into_acid_bits = 0; // nothing can move into acid
7185     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7186
7187     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7188     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7189     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7190     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7191
7192     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7193       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7194
7195     // correct trigger settings (stored as zero == "none" in old levels)
7196
7197     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7198     {
7199       int element = EL_CUSTOM_START + i;
7200       struct ElementInfo *ei = &element_info[element];
7201
7202       for (j = 0; j < ei->num_change_pages; j++)
7203       {
7204         struct ElementChangeInfo *change = &ei->change_page[j];
7205
7206         change->trigger_player = CH_PLAYER_ANY;
7207         change->trigger_page = CH_PAGE_ANY;
7208       }
7209     }
7210   }
7211
7212   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7213   {
7214     int element = EL_CUSTOM_256;
7215     struct ElementInfo *ei = &element_info[element];
7216     struct ElementChangeInfo *change = &ei->change_page[0];
7217
7218     /* This is needed to fix a problem that was caused by a bugfix in function
7219        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7220        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7221        not replace walkable elements, but instead just placed the player on it,
7222        without placing the Sokoban field under the player). Unfortunately, this
7223        breaks "Snake Bite" style levels when the snake is halfway through a door
7224        that just closes (the snake head is still alive and can be moved in this
7225        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7226        player (without Sokoban element) which then gets killed as designed). */
7227
7228     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7229          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7230         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7231       change->target_element = EL_PLAYER_1;
7232   }
7233
7234   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7235   if (level->game_version < VERSION_IDENT(3,2,5,0))
7236   {
7237     /* This is needed to fix a problem that was caused by a bugfix in function
7238        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7239        corrects the behaviour when a custom element changes to another custom
7240        element with a higher element number that has change actions defined.
7241        Normally, only one change per frame is allowed for custom elements.
7242        Therefore, it is checked if a custom element already changed in the
7243        current frame; if it did, subsequent changes are suppressed.
7244        Unfortunately, this is only checked for element changes, but not for
7245        change actions, which are still executed. As the function above loops
7246        through all custom elements from lower to higher, an element change
7247        resulting in a lower CE number won't be checked again, while a target
7248        element with a higher number will also be checked, and potential change
7249        actions will get executed for this CE, too (which is wrong), while
7250        further changes are ignored (which is correct). As this bugfix breaks
7251        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7252        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7253        behaviour for existing levels and tapes that make use of this bug */
7254
7255     level->use_action_after_change_bug = TRUE;
7256   }
7257
7258   // not centering level after relocating player was default only in 3.2.3
7259   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7260     level->shifted_relocation = TRUE;
7261
7262   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7263   if (level->game_version < VERSION_IDENT(3,2,6,0))
7264     level->em_explodes_by_fire = TRUE;
7265
7266   // levels were solved by the first player entering an exit up to 4.1.0.0
7267   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7268     level->solved_by_one_player = TRUE;
7269
7270   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7271   if (level->game_version < VERSION_IDENT(4,1,1,1))
7272     level->use_life_bugs = TRUE;
7273
7274   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7275   if (level->game_version < VERSION_IDENT(4,1,1,1))
7276     level->sb_objects_needed = FALSE;
7277
7278   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7279   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7280     level->finish_dig_collect = FALSE;
7281
7282   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7283   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7284     level->keep_walkable_ce = TRUE;
7285 }
7286
7287 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7288 {
7289   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7290   int x, y;
7291
7292   // check if this level is (not) a Sokoban level
7293   for (y = 0; y < level->fieldy; y++)
7294     for (x = 0; x < level->fieldx; x++)
7295       if (!IS_SB_ELEMENT(Tile[x][y]))
7296         is_sokoban_level = FALSE;
7297
7298   if (is_sokoban_level)
7299   {
7300     // set special level settings for Sokoban levels
7301     SetLevelSettings_SB(level);
7302   }
7303 }
7304
7305 static void LoadLevel_InitSettings(struct LevelInfo *level)
7306 {
7307   // adjust level settings for (non-native) Sokoban-style levels
7308   LoadLevel_InitSettings_SB(level);
7309
7310   // rename levels with title "nameless level" or if renaming is forced
7311   if (leveldir_current->empty_level_name != NULL &&
7312       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7313        leveldir_current->force_level_name))
7314     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7315              leveldir_current->empty_level_name, level_nr);
7316 }
7317
7318 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7319 {
7320   int i, x, y;
7321
7322   // map elements that have changed in newer versions
7323   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7324                                                     level->game_version);
7325   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7326     for (x = 0; x < 3; x++)
7327       for (y = 0; y < 3; y++)
7328         level->yamyam_content[i].e[x][y] =
7329           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7330                                     level->game_version);
7331
7332 }
7333
7334 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7335 {
7336   int i, j;
7337
7338   // map custom element change events that have changed in newer versions
7339   // (these following values were accidentally changed in version 3.0.1)
7340   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7341   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7342   {
7343     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7344     {
7345       int element = EL_CUSTOM_START + i;
7346
7347       // order of checking and copying events to be mapped is important
7348       // (do not change the start and end value -- they are constant)
7349       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7350       {
7351         if (HAS_CHANGE_EVENT(element, j - 2))
7352         {
7353           SET_CHANGE_EVENT(element, j - 2, FALSE);
7354           SET_CHANGE_EVENT(element, j, TRUE);
7355         }
7356       }
7357
7358       // order of checking and copying events to be mapped is important
7359       // (do not change the start and end value -- they are constant)
7360       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7361       {
7362         if (HAS_CHANGE_EVENT(element, j - 1))
7363         {
7364           SET_CHANGE_EVENT(element, j - 1, FALSE);
7365           SET_CHANGE_EVENT(element, j, TRUE);
7366         }
7367       }
7368     }
7369   }
7370
7371   // initialize "can_change" field for old levels with only one change page
7372   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7373   {
7374     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7375     {
7376       int element = EL_CUSTOM_START + i;
7377
7378       if (CAN_CHANGE(element))
7379         element_info[element].change->can_change = TRUE;
7380     }
7381   }
7382
7383   // correct custom element values (for old levels without these options)
7384   if (level->game_version < VERSION_IDENT(3,1,1,0))
7385   {
7386     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7387     {
7388       int element = EL_CUSTOM_START + i;
7389       struct ElementInfo *ei = &element_info[element];
7390
7391       if (ei->access_direction == MV_NO_DIRECTION)
7392         ei->access_direction = MV_ALL_DIRECTIONS;
7393     }
7394   }
7395
7396   // correct custom element values (fix invalid values for all versions)
7397   if (1)
7398   {
7399     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7400     {
7401       int element = EL_CUSTOM_START + i;
7402       struct ElementInfo *ei = &element_info[element];
7403
7404       for (j = 0; j < ei->num_change_pages; j++)
7405       {
7406         struct ElementChangeInfo *change = &ei->change_page[j];
7407
7408         if (change->trigger_player == CH_PLAYER_NONE)
7409           change->trigger_player = CH_PLAYER_ANY;
7410
7411         if (change->trigger_side == CH_SIDE_NONE)
7412           change->trigger_side = CH_SIDE_ANY;
7413       }
7414     }
7415   }
7416
7417   // initialize "can_explode" field for old levels which did not store this
7418   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7419   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7420   {
7421     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7422     {
7423       int element = EL_CUSTOM_START + i;
7424
7425       if (EXPLODES_1X1_OLD(element))
7426         element_info[element].explosion_type = EXPLODES_1X1;
7427
7428       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7429                                              EXPLODES_SMASHED(element) ||
7430                                              EXPLODES_IMPACT(element)));
7431     }
7432   }
7433
7434   // correct previously hard-coded move delay values for maze runner style
7435   if (level->game_version < VERSION_IDENT(3,1,1,0))
7436   {
7437     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7438     {
7439       int element = EL_CUSTOM_START + i;
7440
7441       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7442       {
7443         // previously hard-coded and therefore ignored
7444         element_info[element].move_delay_fixed = 9;
7445         element_info[element].move_delay_random = 0;
7446       }
7447     }
7448   }
7449
7450   // set some other uninitialized values of custom elements in older levels
7451   if (level->game_version < VERSION_IDENT(3,1,0,0))
7452   {
7453     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7454     {
7455       int element = EL_CUSTOM_START + i;
7456
7457       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7458
7459       element_info[element].explosion_delay = 17;
7460       element_info[element].ignition_delay = 8;
7461     }
7462   }
7463
7464   // set mouse click change events to work for left/middle/right mouse button
7465   if (level->game_version < VERSION_IDENT(4,2,3,0))
7466   {
7467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7468     {
7469       int element = EL_CUSTOM_START + i;
7470       struct ElementInfo *ei = &element_info[element];
7471
7472       for (j = 0; j < ei->num_change_pages; j++)
7473       {
7474         struct ElementChangeInfo *change = &ei->change_page[j];
7475
7476         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7477             change->has_event[CE_PRESSED_BY_MOUSE] ||
7478             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7479             change->has_event[CE_MOUSE_PRESSED_ON_X])
7480           change->trigger_side = CH_SIDE_ANY;
7481       }
7482     }
7483   }
7484 }
7485
7486 static void LoadLevel_InitElements(struct LevelInfo *level)
7487 {
7488   LoadLevel_InitStandardElements(level);
7489
7490   if (level->file_has_custom_elements)
7491     LoadLevel_InitCustomElements(level);
7492
7493   // initialize element properties for level editor etc.
7494   InitElementPropertiesEngine(level->game_version);
7495   InitElementPropertiesGfxElement();
7496 }
7497
7498 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7499 {
7500   int x, y;
7501
7502   // map elements that have changed in newer versions
7503   for (y = 0; y < level->fieldy; y++)
7504     for (x = 0; x < level->fieldx; x++)
7505       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7506                                                      level->game_version);
7507
7508   // clear unused playfield data (nicer if level gets resized in editor)
7509   for (x = 0; x < MAX_LEV_FIELDX; x++)
7510     for (y = 0; y < MAX_LEV_FIELDY; y++)
7511       if (x >= level->fieldx || y >= level->fieldy)
7512         level->field[x][y] = EL_EMPTY;
7513
7514   // copy elements to runtime playfield array
7515   for (x = 0; x < MAX_LEV_FIELDX; x++)
7516     for (y = 0; y < MAX_LEV_FIELDY; y++)
7517       Tile[x][y] = level->field[x][y];
7518
7519   // initialize level size variables for faster access
7520   lev_fieldx = level->fieldx;
7521   lev_fieldy = level->fieldy;
7522
7523   // determine border element for this level
7524   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7525     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7526   else
7527     SetBorderElement();
7528 }
7529
7530 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7531 {
7532   struct LevelFileInfo *level_file_info = &level->file_info;
7533
7534   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7535     CopyNativeLevel_RND_to_Native(level);
7536 }
7537
7538 static void LoadLevelTemplate_LoadAndInit(void)
7539 {
7540   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7541
7542   LoadLevel_InitVersion(&level_template);
7543   LoadLevel_InitElements(&level_template);
7544   LoadLevel_InitSettings(&level_template);
7545
7546   ActivateLevelTemplate();
7547 }
7548
7549 void LoadLevelTemplate(int nr)
7550 {
7551   if (!fileExists(getGlobalLevelTemplateFilename()))
7552   {
7553     Warn("no level template found for this level");
7554
7555     return;
7556   }
7557
7558   setLevelFileInfo(&level_template.file_info, nr);
7559
7560   LoadLevelTemplate_LoadAndInit();
7561 }
7562
7563 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7564 {
7565   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7566
7567   LoadLevelTemplate_LoadAndInit();
7568 }
7569
7570 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7571 {
7572   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7573
7574   if (level.use_custom_template)
7575   {
7576     if (network_level != NULL)
7577       LoadNetworkLevelTemplate(network_level);
7578     else
7579       LoadLevelTemplate(-1);
7580   }
7581
7582   LoadLevel_InitVersion(&level);
7583   LoadLevel_InitElements(&level);
7584   LoadLevel_InitPlayfield(&level);
7585   LoadLevel_InitSettings(&level);
7586
7587   LoadLevel_InitNativeEngines(&level);
7588 }
7589
7590 void LoadLevel(int nr)
7591 {
7592   SetLevelSetInfo(leveldir_current->identifier, nr);
7593
7594   setLevelFileInfo(&level.file_info, nr);
7595
7596   LoadLevel_LoadAndInit(NULL);
7597 }
7598
7599 void LoadLevelInfoOnly(int nr)
7600 {
7601   setLevelFileInfo(&level.file_info, nr);
7602
7603   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7604 }
7605
7606 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7607 {
7608   SetLevelSetInfo(network_level->leveldir_identifier,
7609                   network_level->file_info.nr);
7610
7611   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7612
7613   LoadLevel_LoadAndInit(network_level);
7614 }
7615
7616 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7617 {
7618   int chunk_size = 0;
7619
7620   chunk_size += putFileVersion(file, level->file_version);
7621   chunk_size += putFileVersion(file, level->game_version);
7622
7623   return chunk_size;
7624 }
7625
7626 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7627 {
7628   int chunk_size = 0;
7629
7630   chunk_size += putFile16BitBE(file, level->creation_date.year);
7631   chunk_size += putFile8Bit(file,    level->creation_date.month);
7632   chunk_size += putFile8Bit(file,    level->creation_date.day);
7633
7634   return chunk_size;
7635 }
7636
7637 #if ENABLE_HISTORIC_CHUNKS
7638 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7639 {
7640   int i, x, y;
7641
7642   putFile8Bit(file, level->fieldx);
7643   putFile8Bit(file, level->fieldy);
7644
7645   putFile16BitBE(file, level->time);
7646   putFile16BitBE(file, level->gems_needed);
7647
7648   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7649     putFile8Bit(file, level->name[i]);
7650
7651   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7652     putFile8Bit(file, level->score[i]);
7653
7654   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7655     for (y = 0; y < 3; y++)
7656       for (x = 0; x < 3; x++)
7657         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7658                            level->yamyam_content[i].e[x][y]));
7659   putFile8Bit(file, level->amoeba_speed);
7660   putFile8Bit(file, level->time_magic_wall);
7661   putFile8Bit(file, level->time_wheel);
7662   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7663                      level->amoeba_content));
7664   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7665   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7666   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7667   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7668
7669   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7670
7671   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7672   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7673   putFile32BitBE(file, level->can_move_into_acid_bits);
7674   putFile8Bit(file, level->dont_collide_with_bits);
7675
7676   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7677   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7678
7679   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7680   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7681   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7682
7683   putFile8Bit(file, level->game_engine_type);
7684
7685   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7686 }
7687 #endif
7688
7689 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7690 {
7691   int chunk_size = 0;
7692   int i;
7693
7694   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7695     chunk_size += putFile8Bit(file, level->name[i]);
7696
7697   return chunk_size;
7698 }
7699
7700 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7701 {
7702   int chunk_size = 0;
7703   int i;
7704
7705   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7706     chunk_size += putFile8Bit(file, level->author[i]);
7707
7708   return chunk_size;
7709 }
7710
7711 #if ENABLE_HISTORIC_CHUNKS
7712 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7713 {
7714   int chunk_size = 0;
7715   int x, y;
7716
7717   for (y = 0; y < level->fieldy; y++)
7718     for (x = 0; x < level->fieldx; x++)
7719       if (level->encoding_16bit_field)
7720         chunk_size += putFile16BitBE(file, level->field[x][y]);
7721       else
7722         chunk_size += putFile8Bit(file, level->field[x][y]);
7723
7724   return chunk_size;
7725 }
7726 #endif
7727
7728 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7729 {
7730   int chunk_size = 0;
7731   int x, y;
7732
7733   for (y = 0; y < level->fieldy; y++) 
7734     for (x = 0; x < level->fieldx; x++) 
7735       chunk_size += putFile16BitBE(file, level->field[x][y]);
7736
7737   return chunk_size;
7738 }
7739
7740 #if ENABLE_HISTORIC_CHUNKS
7741 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7742 {
7743   int i, x, y;
7744
7745   putFile8Bit(file, EL_YAMYAM);
7746   putFile8Bit(file, level->num_yamyam_contents);
7747   putFile8Bit(file, 0);
7748   putFile8Bit(file, 0);
7749
7750   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7751     for (y = 0; y < 3; y++)
7752       for (x = 0; x < 3; x++)
7753         if (level->encoding_16bit_field)
7754           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7755         else
7756           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7757 }
7758 #endif
7759
7760 #if ENABLE_HISTORIC_CHUNKS
7761 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7762 {
7763   int i, x, y;
7764   int num_contents, content_xsize, content_ysize;
7765   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7766
7767   if (element == EL_YAMYAM)
7768   {
7769     num_contents = level->num_yamyam_contents;
7770     content_xsize = 3;
7771     content_ysize = 3;
7772
7773     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7774       for (y = 0; y < 3; y++)
7775         for (x = 0; x < 3; x++)
7776           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7777   }
7778   else if (element == EL_BD_AMOEBA)
7779   {
7780     num_contents = 1;
7781     content_xsize = 1;
7782     content_ysize = 1;
7783
7784     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7785       for (y = 0; y < 3; y++)
7786         for (x = 0; x < 3; x++)
7787           content_array[i][x][y] = EL_EMPTY;
7788     content_array[0][0][0] = level->amoeba_content;
7789   }
7790   else
7791   {
7792     // chunk header already written -- write empty chunk data
7793     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7794
7795     Warn("cannot save content for element '%d'", element);
7796
7797     return;
7798   }
7799
7800   putFile16BitBE(file, element);
7801   putFile8Bit(file, num_contents);
7802   putFile8Bit(file, content_xsize);
7803   putFile8Bit(file, content_ysize);
7804
7805   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7806
7807   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7808     for (y = 0; y < 3; y++)
7809       for (x = 0; x < 3; x++)
7810         putFile16BitBE(file, content_array[i][x][y]);
7811 }
7812 #endif
7813
7814 #if ENABLE_HISTORIC_CHUNKS
7815 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7816 {
7817   int envelope_nr = element - EL_ENVELOPE_1;
7818   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7819   int chunk_size = 0;
7820   int i;
7821
7822   chunk_size += putFile16BitBE(file, element);
7823   chunk_size += putFile16BitBE(file, envelope_len);
7824   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7825   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7826
7827   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7828   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7829
7830   for (i = 0; i < envelope_len; i++)
7831     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7832
7833   return chunk_size;
7834 }
7835 #endif
7836
7837 #if ENABLE_HISTORIC_CHUNKS
7838 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7839                            int num_changed_custom_elements)
7840 {
7841   int i, check = 0;
7842
7843   putFile16BitBE(file, num_changed_custom_elements);
7844
7845   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7846   {
7847     int element = EL_CUSTOM_START + i;
7848
7849     struct ElementInfo *ei = &element_info[element];
7850
7851     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7852     {
7853       if (check < num_changed_custom_elements)
7854       {
7855         putFile16BitBE(file, element);
7856         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7857       }
7858
7859       check++;
7860     }
7861   }
7862
7863   if (check != num_changed_custom_elements)     // should not happen
7864     Warn("inconsistent number of custom element properties");
7865 }
7866 #endif
7867
7868 #if ENABLE_HISTORIC_CHUNKS
7869 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7870                            int num_changed_custom_elements)
7871 {
7872   int i, check = 0;
7873
7874   putFile16BitBE(file, num_changed_custom_elements);
7875
7876   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7877   {
7878     int element = EL_CUSTOM_START + i;
7879
7880     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7881     {
7882       if (check < num_changed_custom_elements)
7883       {
7884         putFile16BitBE(file, element);
7885         putFile16BitBE(file, element_info[element].change->target_element);
7886       }
7887
7888       check++;
7889     }
7890   }
7891
7892   if (check != num_changed_custom_elements)     // should not happen
7893     Warn("inconsistent number of custom target elements");
7894 }
7895 #endif
7896
7897 #if ENABLE_HISTORIC_CHUNKS
7898 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7899                            int num_changed_custom_elements)
7900 {
7901   int i, j, x, y, check = 0;
7902
7903   putFile16BitBE(file, num_changed_custom_elements);
7904
7905   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7906   {
7907     int element = EL_CUSTOM_START + i;
7908     struct ElementInfo *ei = &element_info[element];
7909
7910     if (ei->modified_settings)
7911     {
7912       if (check < num_changed_custom_elements)
7913       {
7914         putFile16BitBE(file, element);
7915
7916         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7917           putFile8Bit(file, ei->description[j]);
7918
7919         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7920
7921         // some free bytes for future properties and padding
7922         WriteUnusedBytesToFile(file, 7);
7923
7924         putFile8Bit(file, ei->use_gfx_element);
7925         putFile16BitBE(file, ei->gfx_element_initial);
7926
7927         putFile8Bit(file, ei->collect_score_initial);
7928         putFile8Bit(file, ei->collect_count_initial);
7929
7930         putFile16BitBE(file, ei->push_delay_fixed);
7931         putFile16BitBE(file, ei->push_delay_random);
7932         putFile16BitBE(file, ei->move_delay_fixed);
7933         putFile16BitBE(file, ei->move_delay_random);
7934
7935         putFile16BitBE(file, ei->move_pattern);
7936         putFile8Bit(file, ei->move_direction_initial);
7937         putFile8Bit(file, ei->move_stepsize);
7938
7939         for (y = 0; y < 3; y++)
7940           for (x = 0; x < 3; x++)
7941             putFile16BitBE(file, ei->content.e[x][y]);
7942
7943         putFile32BitBE(file, ei->change->events);
7944
7945         putFile16BitBE(file, ei->change->target_element);
7946
7947         putFile16BitBE(file, ei->change->delay_fixed);
7948         putFile16BitBE(file, ei->change->delay_random);
7949         putFile16BitBE(file, ei->change->delay_frames);
7950
7951         putFile16BitBE(file, ei->change->initial_trigger_element);
7952
7953         putFile8Bit(file, ei->change->explode);
7954         putFile8Bit(file, ei->change->use_target_content);
7955         putFile8Bit(file, ei->change->only_if_complete);
7956         putFile8Bit(file, ei->change->use_random_replace);
7957
7958         putFile8Bit(file, ei->change->random_percentage);
7959         putFile8Bit(file, ei->change->replace_when);
7960
7961         for (y = 0; y < 3; y++)
7962           for (x = 0; x < 3; x++)
7963             putFile16BitBE(file, ei->change->content.e[x][y]);
7964
7965         putFile8Bit(file, ei->slippery_type);
7966
7967         // some free bytes for future properties and padding
7968         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7969       }
7970
7971       check++;
7972     }
7973   }
7974
7975   if (check != num_changed_custom_elements)     // should not happen
7976     Warn("inconsistent number of custom element properties");
7977 }
7978 #endif
7979
7980 #if ENABLE_HISTORIC_CHUNKS
7981 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7982 {
7983   struct ElementInfo *ei = &element_info[element];
7984   int i, j, x, y;
7985
7986   // ---------- custom element base property values (96 bytes) ----------------
7987
7988   putFile16BitBE(file, element);
7989
7990   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7991     putFile8Bit(file, ei->description[i]);
7992
7993   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7994
7995   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
7996
7997   putFile8Bit(file, ei->num_change_pages);
7998
7999   putFile16BitBE(file, ei->ce_value_fixed_initial);
8000   putFile16BitBE(file, ei->ce_value_random_initial);
8001   putFile8Bit(file, ei->use_last_ce_value);
8002
8003   putFile8Bit(file, ei->use_gfx_element);
8004   putFile16BitBE(file, ei->gfx_element_initial);
8005
8006   putFile8Bit(file, ei->collect_score_initial);
8007   putFile8Bit(file, ei->collect_count_initial);
8008
8009   putFile8Bit(file, ei->drop_delay_fixed);
8010   putFile8Bit(file, ei->push_delay_fixed);
8011   putFile8Bit(file, ei->drop_delay_random);
8012   putFile8Bit(file, ei->push_delay_random);
8013   putFile16BitBE(file, ei->move_delay_fixed);
8014   putFile16BitBE(file, ei->move_delay_random);
8015
8016   // bits 0 - 15 of "move_pattern" ...
8017   putFile16BitBE(file, ei->move_pattern & 0xffff);
8018   putFile8Bit(file, ei->move_direction_initial);
8019   putFile8Bit(file, ei->move_stepsize);
8020
8021   putFile8Bit(file, ei->slippery_type);
8022
8023   for (y = 0; y < 3; y++)
8024     for (x = 0; x < 3; x++)
8025       putFile16BitBE(file, ei->content.e[x][y]);
8026
8027   putFile16BitBE(file, ei->move_enter_element);
8028   putFile16BitBE(file, ei->move_leave_element);
8029   putFile8Bit(file, ei->move_leave_type);
8030
8031   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8032   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8033
8034   putFile8Bit(file, ei->access_direction);
8035
8036   putFile8Bit(file, ei->explosion_delay);
8037   putFile8Bit(file, ei->ignition_delay);
8038   putFile8Bit(file, ei->explosion_type);
8039
8040   // some free bytes for future custom property values and padding
8041   WriteUnusedBytesToFile(file, 1);
8042
8043   // ---------- change page property values (48 bytes) ------------------------
8044
8045   for (i = 0; i < ei->num_change_pages; i++)
8046   {
8047     struct ElementChangeInfo *change = &ei->change_page[i];
8048     unsigned int event_bits;
8049
8050     // bits 0 - 31 of "has_event[]" ...
8051     event_bits = 0;
8052     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8053       if (change->has_event[j])
8054         event_bits |= (1u << j);
8055     putFile32BitBE(file, event_bits);
8056
8057     putFile16BitBE(file, change->target_element);
8058
8059     putFile16BitBE(file, change->delay_fixed);
8060     putFile16BitBE(file, change->delay_random);
8061     putFile16BitBE(file, change->delay_frames);
8062
8063     putFile16BitBE(file, change->initial_trigger_element);
8064
8065     putFile8Bit(file, change->explode);
8066     putFile8Bit(file, change->use_target_content);
8067     putFile8Bit(file, change->only_if_complete);
8068     putFile8Bit(file, change->use_random_replace);
8069
8070     putFile8Bit(file, change->random_percentage);
8071     putFile8Bit(file, change->replace_when);
8072
8073     for (y = 0; y < 3; y++)
8074       for (x = 0; x < 3; x++)
8075         putFile16BitBE(file, change->target_content.e[x][y]);
8076
8077     putFile8Bit(file, change->can_change);
8078
8079     putFile8Bit(file, change->trigger_side);
8080
8081     putFile8Bit(file, change->trigger_player);
8082     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8083                        log_2(change->trigger_page)));
8084
8085     putFile8Bit(file, change->has_action);
8086     putFile8Bit(file, change->action_type);
8087     putFile8Bit(file, change->action_mode);
8088     putFile16BitBE(file, change->action_arg);
8089
8090     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8091     event_bits = 0;
8092     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8093       if (change->has_event[j])
8094         event_bits |= (1u << (j - 32));
8095     putFile8Bit(file, event_bits);
8096   }
8097 }
8098 #endif
8099
8100 #if ENABLE_HISTORIC_CHUNKS
8101 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8102 {
8103   struct ElementInfo *ei = &element_info[element];
8104   struct ElementGroupInfo *group = ei->group;
8105   int i;
8106
8107   putFile16BitBE(file, element);
8108
8109   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8110     putFile8Bit(file, ei->description[i]);
8111
8112   putFile8Bit(file, group->num_elements);
8113
8114   putFile8Bit(file, ei->use_gfx_element);
8115   putFile16BitBE(file, ei->gfx_element_initial);
8116
8117   putFile8Bit(file, group->choice_mode);
8118
8119   // some free bytes for future values and padding
8120   WriteUnusedBytesToFile(file, 3);
8121
8122   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8123     putFile16BitBE(file, group->element[i]);
8124 }
8125 #endif
8126
8127 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8128                                 boolean write_element)
8129 {
8130   int save_type = entry->save_type;
8131   int data_type = entry->data_type;
8132   int conf_type = entry->conf_type;
8133   int byte_mask = conf_type & CONF_MASK_BYTES;
8134   int element = entry->element;
8135   int default_value = entry->default_value;
8136   int num_bytes = 0;
8137   boolean modified = FALSE;
8138
8139   if (byte_mask != CONF_MASK_MULTI_BYTES)
8140   {
8141     void *value_ptr = entry->value;
8142     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8143                  *(int *)value_ptr);
8144
8145     // check if any settings have been modified before saving them
8146     if (value != default_value)
8147       modified = TRUE;
8148
8149     // do not save if explicitly told or if unmodified default settings
8150     if ((save_type == SAVE_CONF_NEVER) ||
8151         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8152       return 0;
8153
8154     if (write_element)
8155       num_bytes += putFile16BitBE(file, element);
8156
8157     num_bytes += putFile8Bit(file, conf_type);
8158     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8159                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8160                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8161                   0);
8162   }
8163   else if (data_type == TYPE_STRING)
8164   {
8165     char *default_string = entry->default_string;
8166     char *string = (char *)(entry->value);
8167     int string_length = strlen(string);
8168     int i;
8169
8170     // check if any settings have been modified before saving them
8171     if (!strEqual(string, default_string))
8172       modified = TRUE;
8173
8174     // do not save if explicitly told or if unmodified default settings
8175     if ((save_type == SAVE_CONF_NEVER) ||
8176         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8177       return 0;
8178
8179     if (write_element)
8180       num_bytes += putFile16BitBE(file, element);
8181
8182     num_bytes += putFile8Bit(file, conf_type);
8183     num_bytes += putFile16BitBE(file, string_length);
8184
8185     for (i = 0; i < string_length; i++)
8186       num_bytes += putFile8Bit(file, string[i]);
8187   }
8188   else if (data_type == TYPE_ELEMENT_LIST)
8189   {
8190     int *element_array = (int *)(entry->value);
8191     int num_elements = *(int *)(entry->num_entities);
8192     int i;
8193
8194     // check if any settings have been modified before saving them
8195     for (i = 0; i < num_elements; i++)
8196       if (element_array[i] != default_value)
8197         modified = TRUE;
8198
8199     // do not save if explicitly told or if unmodified default settings
8200     if ((save_type == SAVE_CONF_NEVER) ||
8201         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8202       return 0;
8203
8204     if (write_element)
8205       num_bytes += putFile16BitBE(file, element);
8206
8207     num_bytes += putFile8Bit(file, conf_type);
8208     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8209
8210     for (i = 0; i < num_elements; i++)
8211       num_bytes += putFile16BitBE(file, element_array[i]);
8212   }
8213   else if (data_type == TYPE_CONTENT_LIST)
8214   {
8215     struct Content *content = (struct Content *)(entry->value);
8216     int num_contents = *(int *)(entry->num_entities);
8217     int i, x, y;
8218
8219     // check if any settings have been modified before saving them
8220     for (i = 0; i < num_contents; i++)
8221       for (y = 0; y < 3; y++)
8222         for (x = 0; x < 3; x++)
8223           if (content[i].e[x][y] != default_value)
8224             modified = TRUE;
8225
8226     // do not save if explicitly told or if unmodified default settings
8227     if ((save_type == SAVE_CONF_NEVER) ||
8228         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8229       return 0;
8230
8231     if (write_element)
8232       num_bytes += putFile16BitBE(file, element);
8233
8234     num_bytes += putFile8Bit(file, conf_type);
8235     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8236
8237     for (i = 0; i < num_contents; i++)
8238       for (y = 0; y < 3; y++)
8239         for (x = 0; x < 3; x++)
8240           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8241   }
8242
8243   return num_bytes;
8244 }
8245
8246 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8247 {
8248   int chunk_size = 0;
8249   int i;
8250
8251   li = *level;          // copy level data into temporary buffer
8252
8253   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8254     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8255
8256   return chunk_size;
8257 }
8258
8259 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8260 {
8261   int chunk_size = 0;
8262   int i;
8263
8264   li = *level;          // copy level data into temporary buffer
8265
8266   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8267     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8268
8269   return chunk_size;
8270 }
8271
8272 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8273 {
8274   int envelope_nr = element - EL_ENVELOPE_1;
8275   int chunk_size = 0;
8276   int i;
8277
8278   chunk_size += putFile16BitBE(file, element);
8279
8280   // copy envelope data into temporary buffer
8281   xx_envelope = level->envelope[envelope_nr];
8282
8283   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8284     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8285
8286   return chunk_size;
8287 }
8288
8289 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8290 {
8291   struct ElementInfo *ei = &element_info[element];
8292   int chunk_size = 0;
8293   int i, j;
8294
8295   chunk_size += putFile16BitBE(file, element);
8296
8297   xx_ei = *ei;          // copy element data into temporary buffer
8298
8299   // set default description string for this specific element
8300   strcpy(xx_default_description, getDefaultElementDescription(ei));
8301
8302   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8303     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8304
8305   for (i = 0; i < ei->num_change_pages; i++)
8306   {
8307     struct ElementChangeInfo *change = &ei->change_page[i];
8308
8309     xx_current_change_page = i;
8310
8311     xx_change = *change;        // copy change data into temporary buffer
8312
8313     resetEventBits();
8314     setEventBitsFromEventFlags(change);
8315
8316     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8317       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8318                                          FALSE);
8319   }
8320
8321   return chunk_size;
8322 }
8323
8324 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8325 {
8326   struct ElementInfo *ei = &element_info[element];
8327   struct ElementGroupInfo *group = ei->group;
8328   int chunk_size = 0;
8329   int i;
8330
8331   chunk_size += putFile16BitBE(file, element);
8332
8333   xx_ei = *ei;          // copy element data into temporary buffer
8334   xx_group = *group;    // copy group data into temporary buffer
8335
8336   // set default description string for this specific element
8337   strcpy(xx_default_description, getDefaultElementDescription(ei));
8338
8339   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8340     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8341
8342   return chunk_size;
8343 }
8344
8345 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8346 {
8347   struct ElementInfo *ei = &element_info[element];
8348   int chunk_size = 0;
8349   int i;
8350
8351   chunk_size += putFile16BitBE(file, element);
8352
8353   xx_ei = *ei;          // copy element data into temporary buffer
8354
8355   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8356     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8357
8358   return chunk_size;
8359 }
8360
8361 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8362                                   boolean save_as_template)
8363 {
8364   int chunk_size;
8365   int i;
8366   FILE *file;
8367
8368   if (!(file = fopen(filename, MODE_WRITE)))
8369   {
8370     Warn("cannot save level file '%s'", filename);
8371
8372     return;
8373   }
8374
8375   level->file_version = FILE_VERSION_ACTUAL;
8376   level->game_version = GAME_VERSION_ACTUAL;
8377
8378   level->creation_date = getCurrentDate();
8379
8380   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8381   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8382
8383   chunk_size = SaveLevel_VERS(NULL, level);
8384   putFileChunkBE(file, "VERS", chunk_size);
8385   SaveLevel_VERS(file, level);
8386
8387   chunk_size = SaveLevel_DATE(NULL, level);
8388   putFileChunkBE(file, "DATE", chunk_size);
8389   SaveLevel_DATE(file, level);
8390
8391   chunk_size = SaveLevel_NAME(NULL, level);
8392   putFileChunkBE(file, "NAME", chunk_size);
8393   SaveLevel_NAME(file, level);
8394
8395   chunk_size = SaveLevel_AUTH(NULL, level);
8396   putFileChunkBE(file, "AUTH", chunk_size);
8397   SaveLevel_AUTH(file, level);
8398
8399   chunk_size = SaveLevel_INFO(NULL, level);
8400   putFileChunkBE(file, "INFO", chunk_size);
8401   SaveLevel_INFO(file, level);
8402
8403   chunk_size = SaveLevel_BODY(NULL, level);
8404   putFileChunkBE(file, "BODY", chunk_size);
8405   SaveLevel_BODY(file, level);
8406
8407   chunk_size = SaveLevel_ELEM(NULL, level);
8408   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8409   {
8410     putFileChunkBE(file, "ELEM", chunk_size);
8411     SaveLevel_ELEM(file, level);
8412   }
8413
8414   for (i = 0; i < NUM_ENVELOPES; i++)
8415   {
8416     int element = EL_ENVELOPE_1 + i;
8417
8418     chunk_size = SaveLevel_NOTE(NULL, level, element);
8419     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8420     {
8421       putFileChunkBE(file, "NOTE", chunk_size);
8422       SaveLevel_NOTE(file, level, element);
8423     }
8424   }
8425
8426   // if not using template level, check for non-default custom/group elements
8427   if (!level->use_custom_template || save_as_template)
8428   {
8429     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8430     {
8431       int element = EL_CUSTOM_START + i;
8432
8433       chunk_size = SaveLevel_CUSX(NULL, level, element);
8434       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8435       {
8436         putFileChunkBE(file, "CUSX", chunk_size);
8437         SaveLevel_CUSX(file, level, element);
8438       }
8439     }
8440
8441     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8442     {
8443       int element = EL_GROUP_START + i;
8444
8445       chunk_size = SaveLevel_GRPX(NULL, level, element);
8446       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8447       {
8448         putFileChunkBE(file, "GRPX", chunk_size);
8449         SaveLevel_GRPX(file, level, element);
8450       }
8451     }
8452
8453     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8454     {
8455       int element = GET_EMPTY_ELEMENT(i);
8456
8457       chunk_size = SaveLevel_EMPX(NULL, level, element);
8458       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8459       {
8460         putFileChunkBE(file, "EMPX", chunk_size);
8461         SaveLevel_EMPX(file, level, element);
8462       }
8463     }
8464   }
8465
8466   fclose(file);
8467
8468   SetFilePermissions(filename, PERMS_PRIVATE);
8469 }
8470
8471 void SaveLevel(int nr)
8472 {
8473   char *filename = getDefaultLevelFilename(nr);
8474
8475   SaveLevelFromFilename(&level, filename, FALSE);
8476 }
8477
8478 void SaveLevelTemplate(void)
8479 {
8480   char *filename = getLocalLevelTemplateFilename();
8481
8482   SaveLevelFromFilename(&level, filename, TRUE);
8483 }
8484
8485 boolean SaveLevelChecked(int nr)
8486 {
8487   char *filename = getDefaultLevelFilename(nr);
8488   boolean new_level = !fileExists(filename);
8489   boolean level_saved = FALSE;
8490
8491   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8492   {
8493     SaveLevel(nr);
8494
8495     if (new_level)
8496       Request("Level saved!", REQ_CONFIRM);
8497
8498     level_saved = TRUE;
8499   }
8500
8501   return level_saved;
8502 }
8503
8504 void DumpLevel(struct LevelInfo *level)
8505 {
8506   if (level->no_level_file || level->no_valid_file)
8507   {
8508     Warn("cannot dump -- no valid level file found");
8509
8510     return;
8511   }
8512
8513   PrintLine("-", 79);
8514   Print("Level xxx (file version %08d, game version %08d)\n",
8515         level->file_version, level->game_version);
8516   PrintLine("-", 79);
8517
8518   Print("Level author: '%s'\n", level->author);
8519   Print("Level title:  '%s'\n", level->name);
8520   Print("\n");
8521   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8522   Print("\n");
8523   Print("Level time:  %d seconds\n", level->time);
8524   Print("Gems needed: %d\n", level->gems_needed);
8525   Print("\n");
8526   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8527   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8528   Print("Time for light:      %d seconds\n", level->time_light);
8529   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8530   Print("\n");
8531   Print("Amoeba speed: %d\n", level->amoeba_speed);
8532   Print("\n");
8533
8534   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8535   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8536   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8537   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8538   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8539   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8540
8541   if (options.debug)
8542   {
8543     int i, j;
8544
8545     for (i = 0; i < NUM_ENVELOPES; i++)
8546     {
8547       char *text = level->envelope[i].text;
8548       int text_len = strlen(text);
8549       boolean has_text = FALSE;
8550
8551       for (j = 0; j < text_len; j++)
8552         if (text[j] != ' ' && text[j] != '\n')
8553           has_text = TRUE;
8554
8555       if (has_text)
8556       {
8557         Print("\n");
8558         Print("Envelope %d:\n'%s'\n", i + 1, text);
8559       }
8560     }
8561   }
8562
8563   PrintLine("-", 79);
8564 }
8565
8566 void DumpLevels(void)
8567 {
8568   static LevelDirTree *dumplevel_leveldir = NULL;
8569
8570   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8571                                                  global.dumplevel_leveldir);
8572
8573   if (dumplevel_leveldir == NULL)
8574     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8575
8576   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8577       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8578     Fail("no such level number: %d", global.dumplevel_level_nr);
8579
8580   leveldir_current = dumplevel_leveldir;
8581
8582   LoadLevel(global.dumplevel_level_nr);
8583   DumpLevel(&level);
8584
8585   CloseAllAndExit(0);
8586 }
8587
8588
8589 // ============================================================================
8590 // tape file functions
8591 // ============================================================================
8592
8593 static void setTapeInfoToDefaults(void)
8594 {
8595   int i;
8596
8597   // always start with reliable default values (empty tape)
8598   TapeErase();
8599
8600   // default values (also for pre-1.2 tapes) with only the first player
8601   tape.player_participates[0] = TRUE;
8602   for (i = 1; i < MAX_PLAYERS; i++)
8603     tape.player_participates[i] = FALSE;
8604
8605   // at least one (default: the first) player participates in every tape
8606   tape.num_participating_players = 1;
8607
8608   tape.property_bits = TAPE_PROPERTY_NONE;
8609
8610   tape.level_nr = level_nr;
8611   tape.counter = 0;
8612   tape.changed = FALSE;
8613   tape.solved = FALSE;
8614
8615   tape.recording = FALSE;
8616   tape.playing = FALSE;
8617   tape.pausing = FALSE;
8618
8619   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8620   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8621
8622   tape.no_info_chunk = TRUE;
8623   tape.no_valid_file = FALSE;
8624 }
8625
8626 static int getTapePosSize(struct TapeInfo *tape)
8627 {
8628   int tape_pos_size = 0;
8629
8630   if (tape->use_key_actions)
8631     tape_pos_size += tape->num_participating_players;
8632
8633   if (tape->use_mouse_actions)
8634     tape_pos_size += 3;         // x and y position and mouse button mask
8635
8636   tape_pos_size += 1;           // tape action delay value
8637
8638   return tape_pos_size;
8639 }
8640
8641 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8642 {
8643   tape->use_key_actions = FALSE;
8644   tape->use_mouse_actions = FALSE;
8645
8646   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8647     tape->use_key_actions = TRUE;
8648
8649   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8650     tape->use_mouse_actions = TRUE;
8651 }
8652
8653 static int getTapeActionValue(struct TapeInfo *tape)
8654 {
8655   return (tape->use_key_actions &&
8656           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8657           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8658           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8659           TAPE_ACTIONS_DEFAULT);
8660 }
8661
8662 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8663 {
8664   tape->file_version = getFileVersion(file);
8665   tape->game_version = getFileVersion(file);
8666
8667   return chunk_size;
8668 }
8669
8670 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8671 {
8672   int i;
8673
8674   tape->random_seed = getFile32BitBE(file);
8675   tape->date        = getFile32BitBE(file);
8676   tape->length      = getFile32BitBE(file);
8677
8678   // read header fields that are new since version 1.2
8679   if (tape->file_version >= FILE_VERSION_1_2)
8680   {
8681     byte store_participating_players = getFile8Bit(file);
8682     int engine_version;
8683
8684     // since version 1.2, tapes store which players participate in the tape
8685     tape->num_participating_players = 0;
8686     for (i = 0; i < MAX_PLAYERS; i++)
8687     {
8688       tape->player_participates[i] = FALSE;
8689
8690       if (store_participating_players & (1 << i))
8691       {
8692         tape->player_participates[i] = TRUE;
8693         tape->num_participating_players++;
8694       }
8695     }
8696
8697     setTapeActionFlags(tape, getFile8Bit(file));
8698
8699     tape->property_bits = getFile8Bit(file);
8700     tape->solved = getFile8Bit(file);
8701
8702     engine_version = getFileVersion(file);
8703     if (engine_version > 0)
8704       tape->engine_version = engine_version;
8705     else
8706       tape->engine_version = tape->game_version;
8707   }
8708
8709   return chunk_size;
8710 }
8711
8712 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8713 {
8714   tape->scr_fieldx = getFile8Bit(file);
8715   tape->scr_fieldy = getFile8Bit(file);
8716
8717   return chunk_size;
8718 }
8719
8720 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8721 {
8722   char *level_identifier = NULL;
8723   int level_identifier_size;
8724   int i;
8725
8726   tape->no_info_chunk = FALSE;
8727
8728   level_identifier_size = getFile16BitBE(file);
8729
8730   level_identifier = checked_malloc(level_identifier_size);
8731
8732   for (i = 0; i < level_identifier_size; i++)
8733     level_identifier[i] = getFile8Bit(file);
8734
8735   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8736   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8737
8738   checked_free(level_identifier);
8739
8740   tape->level_nr = getFile16BitBE(file);
8741
8742   chunk_size = 2 + level_identifier_size + 2;
8743
8744   return chunk_size;
8745 }
8746
8747 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8748 {
8749   int i, j;
8750   int tape_pos_size = getTapePosSize(tape);
8751   int chunk_size_expected = tape_pos_size * tape->length;
8752
8753   if (chunk_size_expected != chunk_size)
8754   {
8755     ReadUnusedBytesFromFile(file, chunk_size);
8756     return chunk_size_expected;
8757   }
8758
8759   for (i = 0; i < tape->length; i++)
8760   {
8761     if (i >= MAX_TAPE_LEN)
8762     {
8763       Warn("tape truncated -- size exceeds maximum tape size %d",
8764             MAX_TAPE_LEN);
8765
8766       // tape too large; read and ignore remaining tape data from this chunk
8767       for (;i < tape->length; i++)
8768         ReadUnusedBytesFromFile(file, tape_pos_size);
8769
8770       break;
8771     }
8772
8773     if (tape->use_key_actions)
8774     {
8775       for (j = 0; j < MAX_PLAYERS; j++)
8776       {
8777         tape->pos[i].action[j] = MV_NONE;
8778
8779         if (tape->player_participates[j])
8780           tape->pos[i].action[j] = getFile8Bit(file);
8781       }
8782     }
8783
8784     if (tape->use_mouse_actions)
8785     {
8786       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8787       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8788       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8789     }
8790
8791     tape->pos[i].delay = getFile8Bit(file);
8792
8793     if (tape->file_version == FILE_VERSION_1_0)
8794     {
8795       // eliminate possible diagonal moves in old tapes
8796       // this is only for backward compatibility
8797
8798       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8799       byte action = tape->pos[i].action[0];
8800       int k, num_moves = 0;
8801
8802       for (k = 0; k < 4; k++)
8803       {
8804         if (action & joy_dir[k])
8805         {
8806           tape->pos[i + num_moves].action[0] = joy_dir[k];
8807           if (num_moves > 0)
8808             tape->pos[i + num_moves].delay = 0;
8809           num_moves++;
8810         }
8811       }
8812
8813       if (num_moves > 1)
8814       {
8815         num_moves--;
8816         i += num_moves;
8817         tape->length += num_moves;
8818       }
8819     }
8820     else if (tape->file_version < FILE_VERSION_2_0)
8821     {
8822       // convert pre-2.0 tapes to new tape format
8823
8824       if (tape->pos[i].delay > 1)
8825       {
8826         // action part
8827         tape->pos[i + 1] = tape->pos[i];
8828         tape->pos[i + 1].delay = 1;
8829
8830         // delay part
8831         for (j = 0; j < MAX_PLAYERS; j++)
8832           tape->pos[i].action[j] = MV_NONE;
8833         tape->pos[i].delay--;
8834
8835         i++;
8836         tape->length++;
8837       }
8838     }
8839
8840     if (checkEndOfFile(file))
8841       break;
8842   }
8843
8844   if (i != tape->length)
8845     chunk_size = tape_pos_size * i;
8846
8847   return chunk_size;
8848 }
8849
8850 static void LoadTape_SokobanSolution(char *filename)
8851 {
8852   File *file;
8853   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8854
8855   if (!(file = openFile(filename, MODE_READ)))
8856   {
8857     tape.no_valid_file = TRUE;
8858
8859     return;
8860   }
8861
8862   while (!checkEndOfFile(file))
8863   {
8864     unsigned char c = getByteFromFile(file);
8865
8866     if (checkEndOfFile(file))
8867       break;
8868
8869     switch (c)
8870     {
8871       case 'u':
8872       case 'U':
8873         tape.pos[tape.length].action[0] = MV_UP;
8874         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8875         tape.length++;
8876         break;
8877
8878       case 'd':
8879       case 'D':
8880         tape.pos[tape.length].action[0] = MV_DOWN;
8881         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8882         tape.length++;
8883         break;
8884
8885       case 'l':
8886       case 'L':
8887         tape.pos[tape.length].action[0] = MV_LEFT;
8888         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8889         tape.length++;
8890         break;
8891
8892       case 'r':
8893       case 'R':
8894         tape.pos[tape.length].action[0] = MV_RIGHT;
8895         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8896         tape.length++;
8897         break;
8898
8899       case '\n':
8900       case '\r':
8901       case '\t':
8902       case ' ':
8903         // ignore white-space characters
8904         break;
8905
8906       default:
8907         tape.no_valid_file = TRUE;
8908
8909         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8910
8911         break;
8912     }
8913   }
8914
8915   closeFile(file);
8916
8917   if (tape.no_valid_file)
8918     return;
8919
8920   tape.length_frames  = GetTapeLengthFrames();
8921   tape.length_seconds = GetTapeLengthSeconds();
8922 }
8923
8924 void LoadTapeFromFilename(char *filename)
8925 {
8926   char cookie[MAX_LINE_LEN];
8927   char chunk_name[CHUNK_ID_LEN + 1];
8928   File *file;
8929   int chunk_size;
8930
8931   // always start with reliable default values
8932   setTapeInfoToDefaults();
8933
8934   if (strSuffix(filename, ".sln"))
8935   {
8936     LoadTape_SokobanSolution(filename);
8937
8938     return;
8939   }
8940
8941   if (!(file = openFile(filename, MODE_READ)))
8942   {
8943     tape.no_valid_file = TRUE;
8944
8945     return;
8946   }
8947
8948   getFileChunkBE(file, chunk_name, NULL);
8949   if (strEqual(chunk_name, "RND1"))
8950   {
8951     getFile32BitBE(file);               // not used
8952
8953     getFileChunkBE(file, chunk_name, NULL);
8954     if (!strEqual(chunk_name, "TAPE"))
8955     {
8956       tape.no_valid_file = TRUE;
8957
8958       Warn("unknown format of tape file '%s'", filename);
8959
8960       closeFile(file);
8961
8962       return;
8963     }
8964   }
8965   else  // check for pre-2.0 file format with cookie string
8966   {
8967     strcpy(cookie, chunk_name);
8968     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8969       cookie[4] = '\0';
8970     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8971       cookie[strlen(cookie) - 1] = '\0';
8972
8973     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8974     {
8975       tape.no_valid_file = TRUE;
8976
8977       Warn("unknown format of tape file '%s'", filename);
8978
8979       closeFile(file);
8980
8981       return;
8982     }
8983
8984     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8985     {
8986       tape.no_valid_file = TRUE;
8987
8988       Warn("unsupported version of tape file '%s'", filename);
8989
8990       closeFile(file);
8991
8992       return;
8993     }
8994
8995     // pre-2.0 tape files have no game version, so use file version here
8996     tape.game_version = tape.file_version;
8997   }
8998
8999   if (tape.file_version < FILE_VERSION_1_2)
9000   {
9001     // tape files from versions before 1.2.0 without chunk structure
9002     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9003     LoadTape_BODY(file, 2 * tape.length,      &tape);
9004   }
9005   else
9006   {
9007     static struct
9008     {
9009       char *name;
9010       int size;
9011       int (*loader)(File *, int, struct TapeInfo *);
9012     }
9013     chunk_info[] =
9014     {
9015       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9016       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9017       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9018       { "INFO", -1,                     LoadTape_INFO },
9019       { "BODY", -1,                     LoadTape_BODY },
9020       {  NULL,  0,                      NULL }
9021     };
9022
9023     while (getFileChunkBE(file, chunk_name, &chunk_size))
9024     {
9025       int i = 0;
9026
9027       while (chunk_info[i].name != NULL &&
9028              !strEqual(chunk_name, chunk_info[i].name))
9029         i++;
9030
9031       if (chunk_info[i].name == NULL)
9032       {
9033         Warn("unknown chunk '%s' in tape file '%s'",
9034               chunk_name, filename);
9035
9036         ReadUnusedBytesFromFile(file, chunk_size);
9037       }
9038       else if (chunk_info[i].size != -1 &&
9039                chunk_info[i].size != chunk_size)
9040       {
9041         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9042               chunk_size, chunk_name, filename);
9043
9044         ReadUnusedBytesFromFile(file, chunk_size);
9045       }
9046       else
9047       {
9048         // call function to load this tape chunk
9049         int chunk_size_expected =
9050           (chunk_info[i].loader)(file, chunk_size, &tape);
9051
9052         // the size of some chunks cannot be checked before reading other
9053         // chunks first (like "HEAD" and "BODY") that contain some header
9054         // information, so check them here
9055         if (chunk_size_expected != chunk_size)
9056         {
9057           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9058                 chunk_size, chunk_name, filename);
9059         }
9060       }
9061     }
9062   }
9063
9064   closeFile(file);
9065
9066   tape.length_frames  = GetTapeLengthFrames();
9067   tape.length_seconds = GetTapeLengthSeconds();
9068
9069 #if 0
9070   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9071         tape.file_version);
9072   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9073         tape.game_version);
9074   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9075         tape.engine_version);
9076 #endif
9077 }
9078
9079 void LoadTape(int nr)
9080 {
9081   char *filename = getTapeFilename(nr);
9082
9083   LoadTapeFromFilename(filename);
9084 }
9085
9086 void LoadSolutionTape(int nr)
9087 {
9088   char *filename = getSolutionTapeFilename(nr);
9089
9090   LoadTapeFromFilename(filename);
9091
9092   if (TAPE_IS_EMPTY(tape))
9093   {
9094     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9095         level.native_bd_level->replay != NULL)
9096       CopyNativeTape_BD_to_RND(&level);
9097     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9098         level.native_sp_level->demo.is_available)
9099       CopyNativeTape_SP_to_RND(&level);
9100   }
9101 }
9102
9103 void LoadScoreTape(char *score_tape_basename, int nr)
9104 {
9105   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9106
9107   LoadTapeFromFilename(filename);
9108 }
9109
9110 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9111 {
9112   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9113
9114   LoadTapeFromFilename(filename);
9115 }
9116
9117 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9118 {
9119   // chunk required for team mode tapes with non-default screen size
9120   return (tape->num_participating_players > 1 &&
9121           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9122            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9123 }
9124
9125 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9126 {
9127   putFileVersion(file, tape->file_version);
9128   putFileVersion(file, tape->game_version);
9129 }
9130
9131 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9132 {
9133   int i;
9134   byte store_participating_players = 0;
9135
9136   // set bits for participating players for compact storage
9137   for (i = 0; i < MAX_PLAYERS; i++)
9138     if (tape->player_participates[i])
9139       store_participating_players |= (1 << i);
9140
9141   putFile32BitBE(file, tape->random_seed);
9142   putFile32BitBE(file, tape->date);
9143   putFile32BitBE(file, tape->length);
9144
9145   putFile8Bit(file, store_participating_players);
9146
9147   putFile8Bit(file, getTapeActionValue(tape));
9148
9149   putFile8Bit(file, tape->property_bits);
9150   putFile8Bit(file, tape->solved);
9151
9152   putFileVersion(file, tape->engine_version);
9153 }
9154
9155 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9156 {
9157   putFile8Bit(file, tape->scr_fieldx);
9158   putFile8Bit(file, tape->scr_fieldy);
9159 }
9160
9161 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9162 {
9163   int level_identifier_size = strlen(tape->level_identifier) + 1;
9164   int i;
9165
9166   putFile16BitBE(file, level_identifier_size);
9167
9168   for (i = 0; i < level_identifier_size; i++)
9169     putFile8Bit(file, tape->level_identifier[i]);
9170
9171   putFile16BitBE(file, tape->level_nr);
9172 }
9173
9174 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9175 {
9176   int i, j;
9177
9178   for (i = 0; i < tape->length; i++)
9179   {
9180     if (tape->use_key_actions)
9181     {
9182       for (j = 0; j < MAX_PLAYERS; j++)
9183         if (tape->player_participates[j])
9184           putFile8Bit(file, tape->pos[i].action[j]);
9185     }
9186
9187     if (tape->use_mouse_actions)
9188     {
9189       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9190       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9191       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9192     }
9193
9194     putFile8Bit(file, tape->pos[i].delay);
9195   }
9196 }
9197
9198 void SaveTapeToFilename(char *filename)
9199 {
9200   FILE *file;
9201   int tape_pos_size;
9202   int info_chunk_size;
9203   int body_chunk_size;
9204
9205   if (!(file = fopen(filename, MODE_WRITE)))
9206   {
9207     Warn("cannot save level recording file '%s'", filename);
9208
9209     return;
9210   }
9211
9212   tape_pos_size = getTapePosSize(&tape);
9213
9214   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9215   body_chunk_size = tape_pos_size * tape.length;
9216
9217   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9218   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9219
9220   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9221   SaveTape_VERS(file, &tape);
9222
9223   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9224   SaveTape_HEAD(file, &tape);
9225
9226   if (checkSaveTape_SCRN(&tape))
9227   {
9228     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9229     SaveTape_SCRN(file, &tape);
9230   }
9231
9232   putFileChunkBE(file, "INFO", info_chunk_size);
9233   SaveTape_INFO(file, &tape);
9234
9235   putFileChunkBE(file, "BODY", body_chunk_size);
9236   SaveTape_BODY(file, &tape);
9237
9238   fclose(file);
9239
9240   SetFilePermissions(filename, PERMS_PRIVATE);
9241 }
9242
9243 static void SaveTapeExt(char *filename)
9244 {
9245   int i;
9246
9247   tape.file_version = FILE_VERSION_ACTUAL;
9248   tape.game_version = GAME_VERSION_ACTUAL;
9249
9250   tape.num_participating_players = 0;
9251
9252   // count number of participating players
9253   for (i = 0; i < MAX_PLAYERS; i++)
9254     if (tape.player_participates[i])
9255       tape.num_participating_players++;
9256
9257   SaveTapeToFilename(filename);
9258
9259   tape.changed = FALSE;
9260 }
9261
9262 void SaveTape(int nr)
9263 {
9264   char *filename = getTapeFilename(nr);
9265
9266   InitTapeDirectory(leveldir_current->subdir);
9267
9268   SaveTapeExt(filename);
9269 }
9270
9271 void SaveScoreTape(int nr)
9272 {
9273   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9274
9275   // used instead of "leveldir_current->subdir" (for network games)
9276   InitScoreTapeDirectory(levelset.identifier, nr);
9277
9278   SaveTapeExt(filename);
9279 }
9280
9281 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9282                                   unsigned int req_state_added)
9283 {
9284   char *filename = getTapeFilename(nr);
9285   boolean new_tape = !fileExists(filename);
9286   boolean tape_saved = FALSE;
9287
9288   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9289   {
9290     SaveTape(nr);
9291
9292     if (new_tape)
9293       Request(msg_saved, REQ_CONFIRM | req_state_added);
9294
9295     tape_saved = TRUE;
9296   }
9297
9298   return tape_saved;
9299 }
9300
9301 boolean SaveTapeChecked(int nr)
9302 {
9303   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9304 }
9305
9306 boolean SaveTapeChecked_LevelSolved(int nr)
9307 {
9308   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9309                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9310 }
9311
9312 void DumpTape(struct TapeInfo *tape)
9313 {
9314   int tape_frame_counter;
9315   int i, j;
9316
9317   if (tape->no_valid_file)
9318   {
9319     Warn("cannot dump -- no valid tape file found");
9320
9321     return;
9322   }
9323
9324   PrintLine("-", 79);
9325
9326   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9327         tape->level_nr, tape->file_version, tape->game_version);
9328   Print("                  (effective engine version %08d)\n",
9329         tape->engine_version);
9330   Print("Level series identifier: '%s'\n", tape->level_identifier);
9331
9332   Print("Solution tape: %s\n",
9333         tape->solved ? "yes" :
9334         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9335
9336   Print("Special tape properties: ");
9337   if (tape->property_bits == TAPE_PROPERTY_NONE)
9338     Print("[none]");
9339   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9340     Print("[em_random_bug]");
9341   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9342     Print("[game_speed]");
9343   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9344     Print("[pause]");
9345   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9346     Print("[single_step]");
9347   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9348     Print("[snapshot]");
9349   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9350     Print("[replayed]");
9351   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9352     Print("[tas_keys]");
9353   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9354     Print("[small_graphics]");
9355   Print("\n");
9356
9357   int year2 = tape->date / 10000;
9358   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9359   int month_index_raw = (tape->date / 100) % 100;
9360   int month_index = month_index_raw % 12;       // prevent invalid index
9361   int month = month_index + 1;
9362   int day = tape->date % 100;
9363
9364   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9365
9366   PrintLine("-", 79);
9367
9368   tape_frame_counter = 0;
9369
9370   for (i = 0; i < tape->length; i++)
9371   {
9372     if (i >= MAX_TAPE_LEN)
9373       break;
9374
9375     Print("%04d: ", i);
9376
9377     for (j = 0; j < MAX_PLAYERS; j++)
9378     {
9379       if (tape->player_participates[j])
9380       {
9381         int action = tape->pos[i].action[j];
9382
9383         Print("%d:%02x ", j, action);
9384         Print("[%c%c%c%c|%c%c] - ",
9385               (action & JOY_LEFT ? '<' : ' '),
9386               (action & JOY_RIGHT ? '>' : ' '),
9387               (action & JOY_UP ? '^' : ' '),
9388               (action & JOY_DOWN ? 'v' : ' '),
9389               (action & JOY_BUTTON_1 ? '1' : ' '),
9390               (action & JOY_BUTTON_2 ? '2' : ' '));
9391       }
9392     }
9393
9394     Print("(%03d) ", tape->pos[i].delay);
9395     Print("[%05d]\n", tape_frame_counter);
9396
9397     tape_frame_counter += tape->pos[i].delay;
9398   }
9399
9400   PrintLine("-", 79);
9401 }
9402
9403 void DumpTapes(void)
9404 {
9405   static LevelDirTree *dumptape_leveldir = NULL;
9406
9407   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9408                                                 global.dumptape_leveldir);
9409
9410   if (dumptape_leveldir == NULL)
9411     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9412
9413   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9414       global.dumptape_level_nr > dumptape_leveldir->last_level)
9415     Fail("no such level number: %d", global.dumptape_level_nr);
9416
9417   leveldir_current = dumptape_leveldir;
9418
9419   if (options.mytapes)
9420     LoadTape(global.dumptape_level_nr);
9421   else
9422     LoadSolutionTape(global.dumptape_level_nr);
9423
9424   DumpTape(&tape);
9425
9426   CloseAllAndExit(0);
9427 }
9428
9429
9430 // ============================================================================
9431 // score file functions
9432 // ============================================================================
9433
9434 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9435 {
9436   int i;
9437
9438   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9439   {
9440     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9441     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9442     scores->entry[i].score = 0;
9443     scores->entry[i].time = 0;
9444
9445     scores->entry[i].id = -1;
9446     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9447     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9448     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9449     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9450     strcpy(scores->entry[i].country_code, "??");
9451   }
9452
9453   scores->num_entries = 0;
9454   scores->last_added = -1;
9455   scores->last_added_local = -1;
9456
9457   scores->updated = FALSE;
9458   scores->uploaded = FALSE;
9459   scores->tape_downloaded = FALSE;
9460   scores->force_last_added = FALSE;
9461
9462   // The following values are intentionally not reset here:
9463   // - last_level_nr
9464   // - last_entry_nr
9465   // - next_level_nr
9466   // - continue_playing
9467   // - continue_on_return
9468 }
9469
9470 static void setScoreInfoToDefaults(void)
9471 {
9472   setScoreInfoToDefaultsExt(&scores);
9473 }
9474
9475 static void setServerScoreInfoToDefaults(void)
9476 {
9477   setScoreInfoToDefaultsExt(&server_scores);
9478 }
9479
9480 static void LoadScore_OLD(int nr)
9481 {
9482   int i;
9483   char *filename = getScoreFilename(nr);
9484   char cookie[MAX_LINE_LEN];
9485   char line[MAX_LINE_LEN];
9486   char *line_ptr;
9487   FILE *file;
9488
9489   if (!(file = fopen(filename, MODE_READ)))
9490     return;
9491
9492   // check file identifier
9493   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9494     cookie[0] = '\0';
9495   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9496     cookie[strlen(cookie) - 1] = '\0';
9497
9498   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9499   {
9500     Warn("unknown format of score file '%s'", filename);
9501
9502     fclose(file);
9503
9504     return;
9505   }
9506
9507   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9508   {
9509     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9510       Warn("fscanf() failed; %s", strerror(errno));
9511
9512     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9513       line[0] = '\0';
9514
9515     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9516       line[strlen(line) - 1] = '\0';
9517
9518     for (line_ptr = line; *line_ptr; line_ptr++)
9519     {
9520       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9521       {
9522         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9523         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9524         break;
9525       }
9526     }
9527   }
9528
9529   fclose(file);
9530 }
9531
9532 static void ConvertScore_OLD(void)
9533 {
9534   // only convert score to time for levels that rate playing time over score
9535   if (!level.rate_time_over_score)
9536     return;
9537
9538   // convert old score to playing time for score-less levels (like Supaplex)
9539   int time_final_max = 999;
9540   int i;
9541
9542   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9543   {
9544     int score = scores.entry[i].score;
9545
9546     if (score > 0 && score < time_final_max)
9547       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9548   }
9549 }
9550
9551 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9552 {
9553   scores->file_version = getFileVersion(file);
9554   scores->game_version = getFileVersion(file);
9555
9556   return chunk_size;
9557 }
9558
9559 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9560 {
9561   char *level_identifier = NULL;
9562   int level_identifier_size;
9563   int i;
9564
9565   level_identifier_size = getFile16BitBE(file);
9566
9567   level_identifier = checked_malloc(level_identifier_size);
9568
9569   for (i = 0; i < level_identifier_size; i++)
9570     level_identifier[i] = getFile8Bit(file);
9571
9572   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9573   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9574
9575   checked_free(level_identifier);
9576
9577   scores->level_nr = getFile16BitBE(file);
9578   scores->num_entries = getFile16BitBE(file);
9579
9580   chunk_size = 2 + level_identifier_size + 2 + 2;
9581
9582   return chunk_size;
9583 }
9584
9585 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9586 {
9587   int i, j;
9588
9589   for (i = 0; i < scores->num_entries; i++)
9590   {
9591     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9592       scores->entry[i].name[j] = getFile8Bit(file);
9593
9594     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9595   }
9596
9597   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9598
9599   return chunk_size;
9600 }
9601
9602 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9603 {
9604   int i;
9605
9606   for (i = 0; i < scores->num_entries; i++)
9607     scores->entry[i].score = getFile16BitBE(file);
9608
9609   chunk_size = scores->num_entries * 2;
9610
9611   return chunk_size;
9612 }
9613
9614 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9615 {
9616   int i;
9617
9618   for (i = 0; i < scores->num_entries; i++)
9619     scores->entry[i].score = getFile32BitBE(file);
9620
9621   chunk_size = scores->num_entries * 4;
9622
9623   return chunk_size;
9624 }
9625
9626 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9627 {
9628   int i;
9629
9630   for (i = 0; i < scores->num_entries; i++)
9631     scores->entry[i].time = getFile32BitBE(file);
9632
9633   chunk_size = scores->num_entries * 4;
9634
9635   return chunk_size;
9636 }
9637
9638 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9639 {
9640   int i, j;
9641
9642   for (i = 0; i < scores->num_entries; i++)
9643   {
9644     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9645       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9646
9647     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9648   }
9649
9650   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9651
9652   return chunk_size;
9653 }
9654
9655 void LoadScore(int nr)
9656 {
9657   char *filename = getScoreFilename(nr);
9658   char cookie[MAX_LINE_LEN];
9659   char chunk_name[CHUNK_ID_LEN + 1];
9660   int chunk_size;
9661   boolean old_score_file_format = FALSE;
9662   File *file;
9663
9664   // always start with reliable default values
9665   setScoreInfoToDefaults();
9666
9667   if (!(file = openFile(filename, MODE_READ)))
9668     return;
9669
9670   getFileChunkBE(file, chunk_name, NULL);
9671   if (strEqual(chunk_name, "RND1"))
9672   {
9673     getFile32BitBE(file);               // not used
9674
9675     getFileChunkBE(file, chunk_name, NULL);
9676     if (!strEqual(chunk_name, "SCOR"))
9677     {
9678       Warn("unknown format of score file '%s'", filename);
9679
9680       closeFile(file);
9681
9682       return;
9683     }
9684   }
9685   else  // check for old file format with cookie string
9686   {
9687     strcpy(cookie, chunk_name);
9688     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9689       cookie[4] = '\0';
9690     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9691       cookie[strlen(cookie) - 1] = '\0';
9692
9693     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9694     {
9695       Warn("unknown format of score file '%s'", filename);
9696
9697       closeFile(file);
9698
9699       return;
9700     }
9701
9702     old_score_file_format = TRUE;
9703   }
9704
9705   if (old_score_file_format)
9706   {
9707     // score files from versions before 4.2.4.0 without chunk structure
9708     LoadScore_OLD(nr);
9709
9710     // convert score to time, if possible (mainly for Supaplex levels)
9711     ConvertScore_OLD();
9712   }
9713   else
9714   {
9715     static struct
9716     {
9717       char *name;
9718       int size;
9719       int (*loader)(File *, int, struct ScoreInfo *);
9720     }
9721     chunk_info[] =
9722     {
9723       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9724       { "INFO", -1,                     LoadScore_INFO },
9725       { "NAME", -1,                     LoadScore_NAME },
9726       { "SCOR", -1,                     LoadScore_SCOR },
9727       { "SC4R", -1,                     LoadScore_SC4R },
9728       { "TIME", -1,                     LoadScore_TIME },
9729       { "TAPE", -1,                     LoadScore_TAPE },
9730
9731       {  NULL,  0,                      NULL }
9732     };
9733
9734     while (getFileChunkBE(file, chunk_name, &chunk_size))
9735     {
9736       int i = 0;
9737
9738       while (chunk_info[i].name != NULL &&
9739              !strEqual(chunk_name, chunk_info[i].name))
9740         i++;
9741
9742       if (chunk_info[i].name == NULL)
9743       {
9744         Warn("unknown chunk '%s' in score file '%s'",
9745               chunk_name, filename);
9746
9747         ReadUnusedBytesFromFile(file, chunk_size);
9748       }
9749       else if (chunk_info[i].size != -1 &&
9750                chunk_info[i].size != chunk_size)
9751       {
9752         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9753               chunk_size, chunk_name, filename);
9754
9755         ReadUnusedBytesFromFile(file, chunk_size);
9756       }
9757       else
9758       {
9759         // call function to load this score chunk
9760         int chunk_size_expected =
9761           (chunk_info[i].loader)(file, chunk_size, &scores);
9762
9763         // the size of some chunks cannot be checked before reading other
9764         // chunks first (like "HEAD" and "BODY") that contain some header
9765         // information, so check them here
9766         if (chunk_size_expected != chunk_size)
9767         {
9768           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9769                 chunk_size, chunk_name, filename);
9770         }
9771       }
9772     }
9773   }
9774
9775   closeFile(file);
9776 }
9777
9778 #if ENABLE_HISTORIC_CHUNKS
9779 void SaveScore_OLD(int nr)
9780 {
9781   int i;
9782   char *filename = getScoreFilename(nr);
9783   FILE *file;
9784
9785   // used instead of "leveldir_current->subdir" (for network games)
9786   InitScoreDirectory(levelset.identifier);
9787
9788   if (!(file = fopen(filename, MODE_WRITE)))
9789   {
9790     Warn("cannot save score for level %d", nr);
9791
9792     return;
9793   }
9794
9795   fprintf(file, "%s\n\n", SCORE_COOKIE);
9796
9797   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9798     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9799
9800   fclose(file);
9801
9802   SetFilePermissions(filename, PERMS_PRIVATE);
9803 }
9804 #endif
9805
9806 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9807 {
9808   putFileVersion(file, scores->file_version);
9809   putFileVersion(file, scores->game_version);
9810 }
9811
9812 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9813 {
9814   int level_identifier_size = strlen(scores->level_identifier) + 1;
9815   int i;
9816
9817   putFile16BitBE(file, level_identifier_size);
9818
9819   for (i = 0; i < level_identifier_size; i++)
9820     putFile8Bit(file, scores->level_identifier[i]);
9821
9822   putFile16BitBE(file, scores->level_nr);
9823   putFile16BitBE(file, scores->num_entries);
9824 }
9825
9826 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9827 {
9828   int i, j;
9829
9830   for (i = 0; i < scores->num_entries; i++)
9831   {
9832     int name_size = strlen(scores->entry[i].name);
9833
9834     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9835       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9836   }
9837 }
9838
9839 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9840 {
9841   int i;
9842
9843   for (i = 0; i < scores->num_entries; i++)
9844     putFile16BitBE(file, scores->entry[i].score);
9845 }
9846
9847 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9848 {
9849   int i;
9850
9851   for (i = 0; i < scores->num_entries; i++)
9852     putFile32BitBE(file, scores->entry[i].score);
9853 }
9854
9855 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9856 {
9857   int i;
9858
9859   for (i = 0; i < scores->num_entries; i++)
9860     putFile32BitBE(file, scores->entry[i].time);
9861 }
9862
9863 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9864 {
9865   int i, j;
9866
9867   for (i = 0; i < scores->num_entries; i++)
9868   {
9869     int size = strlen(scores->entry[i].tape_basename);
9870
9871     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9872       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9873   }
9874 }
9875
9876 static void SaveScoreToFilename(char *filename)
9877 {
9878   FILE *file;
9879   int info_chunk_size;
9880   int name_chunk_size;
9881   int scor_chunk_size;
9882   int sc4r_chunk_size;
9883   int time_chunk_size;
9884   int tape_chunk_size;
9885   boolean has_large_score_values;
9886   int i;
9887
9888   if (!(file = fopen(filename, MODE_WRITE)))
9889   {
9890     Warn("cannot save score file '%s'", filename);
9891
9892     return;
9893   }
9894
9895   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9896   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9897   scor_chunk_size = scores.num_entries * 2;
9898   sc4r_chunk_size = scores.num_entries * 4;
9899   time_chunk_size = scores.num_entries * 4;
9900   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9901
9902   has_large_score_values = FALSE;
9903   for (i = 0; i < scores.num_entries; i++)
9904     if (scores.entry[i].score > 0xffff)
9905       has_large_score_values = TRUE;
9906
9907   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9908   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9909
9910   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9911   SaveScore_VERS(file, &scores);
9912
9913   putFileChunkBE(file, "INFO", info_chunk_size);
9914   SaveScore_INFO(file, &scores);
9915
9916   putFileChunkBE(file, "NAME", name_chunk_size);
9917   SaveScore_NAME(file, &scores);
9918
9919   if (has_large_score_values)
9920   {
9921     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9922     SaveScore_SC4R(file, &scores);
9923   }
9924   else
9925   {
9926     putFileChunkBE(file, "SCOR", scor_chunk_size);
9927     SaveScore_SCOR(file, &scores);
9928   }
9929
9930   putFileChunkBE(file, "TIME", time_chunk_size);
9931   SaveScore_TIME(file, &scores);
9932
9933   putFileChunkBE(file, "TAPE", tape_chunk_size);
9934   SaveScore_TAPE(file, &scores);
9935
9936   fclose(file);
9937
9938   SetFilePermissions(filename, PERMS_PRIVATE);
9939 }
9940
9941 void SaveScore(int nr)
9942 {
9943   char *filename = getScoreFilename(nr);
9944   int i;
9945
9946   // used instead of "leveldir_current->subdir" (for network games)
9947   InitScoreDirectory(levelset.identifier);
9948
9949   scores.file_version = FILE_VERSION_ACTUAL;
9950   scores.game_version = GAME_VERSION_ACTUAL;
9951
9952   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9953   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9954   scores.level_nr = level_nr;
9955
9956   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9957     if (scores.entry[i].score == 0 &&
9958         scores.entry[i].time == 0 &&
9959         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9960       break;
9961
9962   scores.num_entries = i;
9963
9964   if (scores.num_entries == 0)
9965     return;
9966
9967   SaveScoreToFilename(filename);
9968 }
9969
9970 static void LoadServerScoreFromCache(int nr)
9971 {
9972   struct ScoreEntry score_entry;
9973   struct
9974   {
9975     void *value;
9976     boolean is_string;
9977     int string_size;
9978   }
9979   score_mapping[] =
9980   {
9981     { &score_entry.score,               FALSE,  0                       },
9982     { &score_entry.time,                FALSE,  0                       },
9983     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
9984     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
9985     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
9986     { &score_entry.id,                  FALSE,  0                       },
9987     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
9988     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
9989     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
9990     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
9991
9992     { NULL,                             FALSE,  0                       }
9993   };
9994   char *filename = getScoreCacheFilename(nr);
9995   SetupFileHash *score_hash = loadSetupFileHash(filename);
9996   int i, j;
9997
9998   server_scores.num_entries = 0;
9999
10000   if (score_hash == NULL)
10001     return;
10002
10003   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10004   {
10005     score_entry = server_scores.entry[i];
10006
10007     for (j = 0; score_mapping[j].value != NULL; j++)
10008     {
10009       char token[10];
10010
10011       sprintf(token, "%02d.%d", i, j);
10012
10013       char *value = getHashEntry(score_hash, token);
10014
10015       if (value == NULL)
10016         continue;
10017
10018       if (score_mapping[j].is_string)
10019       {
10020         char *score_value = (char *)score_mapping[j].value;
10021         int value_size = score_mapping[j].string_size;
10022
10023         strncpy(score_value, value, value_size);
10024         score_value[value_size] = '\0';
10025       }
10026       else
10027       {
10028         int *score_value = (int *)score_mapping[j].value;
10029
10030         *score_value = atoi(value);
10031       }
10032
10033       server_scores.num_entries = i + 1;
10034     }
10035
10036     server_scores.entry[i] = score_entry;
10037   }
10038
10039   freeSetupFileHash(score_hash);
10040 }
10041
10042 void LoadServerScore(int nr, boolean download_score)
10043 {
10044   if (!setup.use_api_server)
10045     return;
10046
10047   // always start with reliable default values
10048   setServerScoreInfoToDefaults();
10049
10050   // 1st step: load server scores from cache file (which may not exist)
10051   // (this should prevent reading it while the thread is writing to it)
10052   LoadServerScoreFromCache(nr);
10053
10054   if (download_score && runtime.use_api_server)
10055   {
10056     // 2nd step: download server scores from score server to cache file
10057     // (as thread, as it might time out if the server is not reachable)
10058     ApiGetScoreAsThread(nr);
10059   }
10060 }
10061
10062 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10063 {
10064   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10065
10066   // if score tape not uploaded, ask for uploading missing tapes later
10067   if (!setup.has_remaining_tapes)
10068     setup.ask_for_remaining_tapes = TRUE;
10069
10070   setup.provide_uploading_tapes = TRUE;
10071   setup.has_remaining_tapes = TRUE;
10072
10073   SaveSetup_ServerSetup();
10074 }
10075
10076 void SaveServerScore(int nr, boolean tape_saved)
10077 {
10078   if (!runtime.use_api_server)
10079   {
10080     PrepareScoreTapesForUpload(leveldir_current->subdir);
10081
10082     return;
10083   }
10084
10085   ApiAddScoreAsThread(nr, tape_saved, NULL);
10086 }
10087
10088 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10089                              char *score_tape_filename)
10090 {
10091   if (!runtime.use_api_server)
10092     return;
10093
10094   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10095 }
10096
10097 void LoadLocalAndServerScore(int nr, boolean download_score)
10098 {
10099   int last_added_local = scores.last_added_local;
10100   boolean force_last_added = scores.force_last_added;
10101
10102   // needed if only showing server scores
10103   setScoreInfoToDefaults();
10104
10105   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10106     LoadScore(nr);
10107
10108   // restore last added local score entry (before merging server scores)
10109   scores.last_added = scores.last_added_local = last_added_local;
10110
10111   if (setup.use_api_server &&
10112       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10113   {
10114     // load server scores from cache file and trigger update from server
10115     LoadServerScore(nr, download_score);
10116
10117     // merge local scores with scores from server
10118     MergeServerScore();
10119   }
10120
10121   if (force_last_added)
10122     scores.force_last_added = force_last_added;
10123 }
10124
10125
10126 // ============================================================================
10127 // setup file functions
10128 // ============================================================================
10129
10130 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10131
10132
10133 static struct TokenInfo global_setup_tokens[] =
10134 {
10135   {
10136     TYPE_STRING,
10137     &setup.player_name,                         "player_name"
10138   },
10139   {
10140     TYPE_SWITCH,
10141     &setup.multiple_users,                      "multiple_users"
10142   },
10143   {
10144     TYPE_SWITCH,
10145     &setup.sound,                               "sound"
10146   },
10147   {
10148     TYPE_SWITCH,
10149     &setup.sound_loops,                         "repeating_sound_loops"
10150   },
10151   {
10152     TYPE_SWITCH,
10153     &setup.sound_music,                         "background_music"
10154   },
10155   {
10156     TYPE_SWITCH,
10157     &setup.sound_simple,                        "simple_sound_effects"
10158   },
10159   {
10160     TYPE_SWITCH,
10161     &setup.toons,                               "toons"
10162   },
10163   {
10164     TYPE_SWITCH,
10165     &setup.global_animations,                   "global_animations"
10166   },
10167   {
10168     TYPE_SWITCH,
10169     &setup.scroll_delay,                        "scroll_delay"
10170   },
10171   {
10172     TYPE_SWITCH,
10173     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10174   },
10175   {
10176     TYPE_INTEGER,
10177     &setup.scroll_delay_value,                  "scroll_delay_value"
10178   },
10179   {
10180     TYPE_STRING,
10181     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10182   },
10183   {
10184     TYPE_INTEGER,
10185     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10186   },
10187   {
10188     TYPE_SWITCH,
10189     &setup.fade_screens,                        "fade_screens"
10190   },
10191   {
10192     TYPE_SWITCH,
10193     &setup.autorecord,                          "automatic_tape_recording"
10194   },
10195   {
10196     TYPE_SWITCH,
10197     &setup.autorecord_after_replay,             "autorecord_after_replay"
10198   },
10199   {
10200     TYPE_SWITCH,
10201     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10202   },
10203   {
10204     TYPE_SWITCH,
10205     &setup.show_titlescreen,                    "show_titlescreen"
10206   },
10207   {
10208     TYPE_SWITCH,
10209     &setup.quick_doors,                         "quick_doors"
10210   },
10211   {
10212     TYPE_SWITCH,
10213     &setup.team_mode,                           "team_mode"
10214   },
10215   {
10216     TYPE_SWITCH,
10217     &setup.handicap,                            "handicap"
10218   },
10219   {
10220     TYPE_SWITCH,
10221     &setup.skip_levels,                         "skip_levels"
10222   },
10223   {
10224     TYPE_SWITCH,
10225     &setup.increment_levels,                    "increment_levels"
10226   },
10227   {
10228     TYPE_SWITCH,
10229     &setup.auto_play_next_level,                "auto_play_next_level"
10230   },
10231   {
10232     TYPE_SWITCH,
10233     &setup.count_score_after_game,              "count_score_after_game"
10234   },
10235   {
10236     TYPE_SWITCH,
10237     &setup.show_scores_after_game,              "show_scores_after_game"
10238   },
10239   {
10240     TYPE_SWITCH,
10241     &setup.time_limit,                          "time_limit"
10242   },
10243   {
10244     TYPE_SWITCH,
10245     &setup.fullscreen,                          "fullscreen"
10246   },
10247   {
10248     TYPE_INTEGER,
10249     &setup.window_scaling_percent,              "window_scaling_percent"
10250   },
10251   {
10252     TYPE_STRING,
10253     &setup.window_scaling_quality,              "window_scaling_quality"
10254   },
10255   {
10256     TYPE_STRING,
10257     &setup.screen_rendering_mode,               "screen_rendering_mode"
10258   },
10259   {
10260     TYPE_STRING,
10261     &setup.vsync_mode,                          "vsync_mode"
10262   },
10263   {
10264     TYPE_SWITCH,
10265     &setup.ask_on_escape,                       "ask_on_escape"
10266   },
10267   {
10268     TYPE_SWITCH,
10269     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10270   },
10271   {
10272     TYPE_SWITCH,
10273     &setup.ask_on_game_over,                    "ask_on_game_over"
10274   },
10275   {
10276     TYPE_SWITCH,
10277     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10278   },
10279   {
10280     TYPE_SWITCH,
10281     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10282   },
10283   {
10284     TYPE_SWITCH,
10285     &setup.quick_switch,                        "quick_player_switch"
10286   },
10287   {
10288     TYPE_SWITCH,
10289     &setup.input_on_focus,                      "input_on_focus"
10290   },
10291   {
10292     TYPE_SWITCH,
10293     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10294   },
10295   {
10296     TYPE_SWITCH,
10297     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10298   },
10299   {
10300     TYPE_SWITCH,
10301     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10302   },
10303   {
10304     TYPE_SWITCH,
10305     &setup.game_speed_extended,                 "game_speed_extended"
10306   },
10307   {
10308     TYPE_INTEGER,
10309     &setup.game_frame_delay,                    "game_frame_delay"
10310   },
10311   {
10312     TYPE_SWITCH,
10313     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10314   },
10315   {
10316     TYPE_SWITCH,
10317     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10318   },
10319   {
10320     TYPE_SWITCH,
10321     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10322   },
10323   {
10324     TYPE_SWITCH3,
10325     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10326   },
10327   {
10328     TYPE_SWITCH,
10329     &setup.sp_show_border_elements,             "sp_show_border_elements"
10330   },
10331   {
10332     TYPE_SWITCH,
10333     &setup.small_game_graphics,                 "small_game_graphics"
10334   },
10335   {
10336     TYPE_SWITCH,
10337     &setup.show_load_save_buttons,              "show_load_save_buttons"
10338   },
10339   {
10340     TYPE_SWITCH,
10341     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10342   },
10343   {
10344     TYPE_STRING,
10345     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10346   },
10347   {
10348     TYPE_STRING,
10349     &setup.graphics_set,                        "graphics_set"
10350   },
10351   {
10352     TYPE_STRING,
10353     &setup.sounds_set,                          "sounds_set"
10354   },
10355   {
10356     TYPE_STRING,
10357     &setup.music_set,                           "music_set"
10358   },
10359   {
10360     TYPE_SWITCH3,
10361     &setup.override_level_graphics,             "override_level_graphics"
10362   },
10363   {
10364     TYPE_SWITCH3,
10365     &setup.override_level_sounds,               "override_level_sounds"
10366   },
10367   {
10368     TYPE_SWITCH3,
10369     &setup.override_level_music,                "override_level_music"
10370   },
10371   {
10372     TYPE_INTEGER,
10373     &setup.volume_simple,                       "volume_simple"
10374   },
10375   {
10376     TYPE_INTEGER,
10377     &setup.volume_loops,                        "volume_loops"
10378   },
10379   {
10380     TYPE_INTEGER,
10381     &setup.volume_music,                        "volume_music"
10382   },
10383   {
10384     TYPE_SWITCH,
10385     &setup.network_mode,                        "network_mode"
10386   },
10387   {
10388     TYPE_PLAYER,
10389     &setup.network_player_nr,                   "network_player"
10390   },
10391   {
10392     TYPE_STRING,
10393     &setup.network_server_hostname,             "network_server_hostname"
10394   },
10395   {
10396     TYPE_STRING,
10397     &setup.touch.control_type,                  "touch.control_type"
10398   },
10399   {
10400     TYPE_INTEGER,
10401     &setup.touch.move_distance,                 "touch.move_distance"
10402   },
10403   {
10404     TYPE_INTEGER,
10405     &setup.touch.drop_distance,                 "touch.drop_distance"
10406   },
10407   {
10408     TYPE_INTEGER,
10409     &setup.touch.transparency,                  "touch.transparency"
10410   },
10411   {
10412     TYPE_INTEGER,
10413     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10414   },
10415   {
10416     TYPE_INTEGER,
10417     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10418   },
10419   {
10420     TYPE_INTEGER,
10421     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10422   },
10423   {
10424     TYPE_INTEGER,
10425     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10426   },
10427   {
10428     TYPE_INTEGER,
10429     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10430   },
10431   {
10432     TYPE_INTEGER,
10433     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10434   },
10435   {
10436     TYPE_SWITCH,
10437     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10438   },
10439 };
10440
10441 static struct TokenInfo auto_setup_tokens[] =
10442 {
10443   {
10444     TYPE_INTEGER,
10445     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10446   },
10447 };
10448
10449 static struct TokenInfo server_setup_tokens[] =
10450 {
10451   {
10452     TYPE_STRING,
10453     &setup.player_uuid,                         "player_uuid"
10454   },
10455   {
10456     TYPE_INTEGER,
10457     &setup.player_version,                      "player_version"
10458   },
10459   {
10460     TYPE_SWITCH,
10461     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10462   },
10463   {
10464     TYPE_STRING,
10465     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10466   },
10467   {
10468     TYPE_STRING,
10469     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10470   },
10471   {
10472     TYPE_SWITCH,
10473     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10474   },
10475   {
10476     TYPE_SWITCH,
10477     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10478   },
10479   {
10480     TYPE_SWITCH,
10481     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10482   },
10483   {
10484     TYPE_SWITCH,
10485     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10486   },
10487   {
10488     TYPE_SWITCH,
10489     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10490   },
10491 };
10492
10493 static struct TokenInfo editor_setup_tokens[] =
10494 {
10495   {
10496     TYPE_SWITCH,
10497     &setup.editor.el_classic,                   "editor.el_classic"
10498   },
10499   {
10500     TYPE_SWITCH,
10501     &setup.editor.el_custom,                    "editor.el_custom"
10502   },
10503   {
10504     TYPE_SWITCH,
10505     &setup.editor.el_user_defined,              "editor.el_user_defined"
10506   },
10507   {
10508     TYPE_SWITCH,
10509     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10510   },
10511   {
10512     TYPE_SWITCH,
10513     &setup.editor.el_headlines,                 "editor.el_headlines"
10514   },
10515   {
10516     TYPE_SWITCH,
10517     &setup.editor.show_element_token,           "editor.show_element_token"
10518   },
10519   {
10520     TYPE_SWITCH,
10521     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10522   },
10523 };
10524
10525 static struct TokenInfo editor_cascade_setup_tokens[] =
10526 {
10527   {
10528     TYPE_SWITCH,
10529     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10530   },
10531   {
10532     TYPE_SWITCH,
10533     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10534   },
10535   {
10536     TYPE_SWITCH,
10537     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10538   },
10539   {
10540     TYPE_SWITCH,
10541     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10542   },
10543   {
10544     TYPE_SWITCH,
10545     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10546   },
10547   {
10548     TYPE_SWITCH,
10549     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10550   },
10551   {
10552     TYPE_SWITCH,
10553     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10554   },
10555   {
10556     TYPE_SWITCH,
10557     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10558   },
10559   {
10560     TYPE_SWITCH,
10561     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10562   },
10563   {
10564     TYPE_SWITCH,
10565     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10566   },
10567   {
10568     TYPE_SWITCH,
10569     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10570   },
10571   {
10572     TYPE_SWITCH,
10573     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10574   },
10575   {
10576     TYPE_SWITCH,
10577     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10578   },
10579   {
10580     TYPE_SWITCH,
10581     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10582   },
10583   {
10584     TYPE_SWITCH,
10585     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10586   },
10587   {
10588     TYPE_SWITCH,
10589     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10590   },
10591   {
10592     TYPE_SWITCH,
10593     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10594   },
10595   {
10596     TYPE_SWITCH,
10597     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10598   },
10599   {
10600     TYPE_SWITCH,
10601     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10602   },
10603   {
10604     TYPE_SWITCH,
10605     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10606   },
10607 };
10608
10609 static struct TokenInfo shortcut_setup_tokens[] =
10610 {
10611   {
10612     TYPE_KEY_X11,
10613     &setup.shortcut.save_game,                  "shortcut.save_game"
10614   },
10615   {
10616     TYPE_KEY_X11,
10617     &setup.shortcut.load_game,                  "shortcut.load_game"
10618   },
10619   {
10620     TYPE_KEY_X11,
10621     &setup.shortcut.restart_game,               "shortcut.restart_game"
10622   },
10623   {
10624     TYPE_KEY_X11,
10625     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10626   },
10627   {
10628     TYPE_KEY_X11,
10629     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10630   },
10631   {
10632     TYPE_KEY_X11,
10633     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10634   },
10635   {
10636     TYPE_KEY_X11,
10637     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10638   },
10639   {
10640     TYPE_KEY_X11,
10641     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10642   },
10643   {
10644     TYPE_KEY_X11,
10645     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10646   },
10647   {
10648     TYPE_KEY_X11,
10649     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10650   },
10651   {
10652     TYPE_KEY_X11,
10653     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10654   },
10655   {
10656     TYPE_KEY_X11,
10657     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10658   },
10659   {
10660     TYPE_KEY_X11,
10661     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10662   },
10663   {
10664     TYPE_KEY_X11,
10665     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10666   },
10667   {
10668     TYPE_KEY_X11,
10669     &setup.shortcut.tape_record,                "shortcut.tape_record"
10670   },
10671   {
10672     TYPE_KEY_X11,
10673     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10674   },
10675   {
10676     TYPE_KEY_X11,
10677     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10678   },
10679   {
10680     TYPE_KEY_X11,
10681     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10682   },
10683   {
10684     TYPE_KEY_X11,
10685     &setup.shortcut.sound_music,                "shortcut.sound_music"
10686   },
10687   {
10688     TYPE_KEY_X11,
10689     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10690   },
10691   {
10692     TYPE_KEY_X11,
10693     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10694   },
10695   {
10696     TYPE_KEY_X11,
10697     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10698   },
10699   {
10700     TYPE_KEY_X11,
10701     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10702   },
10703 };
10704
10705 static struct SetupInputInfo setup_input;
10706 static struct TokenInfo player_setup_tokens[] =
10707 {
10708   {
10709     TYPE_BOOLEAN,
10710     &setup_input.use_joystick,                  ".use_joystick"
10711   },
10712   {
10713     TYPE_STRING,
10714     &setup_input.joy.device_name,               ".joy.device_name"
10715   },
10716   {
10717     TYPE_INTEGER,
10718     &setup_input.joy.xleft,                     ".joy.xleft"
10719   },
10720   {
10721     TYPE_INTEGER,
10722     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10723   },
10724   {
10725     TYPE_INTEGER,
10726     &setup_input.joy.xright,                    ".joy.xright"
10727   },
10728   {
10729     TYPE_INTEGER,
10730     &setup_input.joy.yupper,                    ".joy.yupper"
10731   },
10732   {
10733     TYPE_INTEGER,
10734     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10735   },
10736   {
10737     TYPE_INTEGER,
10738     &setup_input.joy.ylower,                    ".joy.ylower"
10739   },
10740   {
10741     TYPE_INTEGER,
10742     &setup_input.joy.snap,                      ".joy.snap_field"
10743   },
10744   {
10745     TYPE_INTEGER,
10746     &setup_input.joy.drop,                      ".joy.place_bomb"
10747   },
10748   {
10749     TYPE_KEY_X11,
10750     &setup_input.key.left,                      ".key.move_left"
10751   },
10752   {
10753     TYPE_KEY_X11,
10754     &setup_input.key.right,                     ".key.move_right"
10755   },
10756   {
10757     TYPE_KEY_X11,
10758     &setup_input.key.up,                        ".key.move_up"
10759   },
10760   {
10761     TYPE_KEY_X11,
10762     &setup_input.key.down,                      ".key.move_down"
10763   },
10764   {
10765     TYPE_KEY_X11,
10766     &setup_input.key.snap,                      ".key.snap_field"
10767   },
10768   {
10769     TYPE_KEY_X11,
10770     &setup_input.key.drop,                      ".key.place_bomb"
10771   },
10772 };
10773
10774 static struct TokenInfo system_setup_tokens[] =
10775 {
10776   {
10777     TYPE_STRING,
10778     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10779   },
10780   {
10781     TYPE_STRING,
10782     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10783   },
10784   {
10785     TYPE_STRING,
10786     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10787   },
10788   {
10789     TYPE_INTEGER,
10790     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10791   },
10792 };
10793
10794 static struct TokenInfo internal_setup_tokens[] =
10795 {
10796   {
10797     TYPE_STRING,
10798     &setup.internal.program_title,              "program_title"
10799   },
10800   {
10801     TYPE_STRING,
10802     &setup.internal.program_version,            "program_version"
10803   },
10804   {
10805     TYPE_STRING,
10806     &setup.internal.program_author,             "program_author"
10807   },
10808   {
10809     TYPE_STRING,
10810     &setup.internal.program_email,              "program_email"
10811   },
10812   {
10813     TYPE_STRING,
10814     &setup.internal.program_website,            "program_website"
10815   },
10816   {
10817     TYPE_STRING,
10818     &setup.internal.program_copyright,          "program_copyright"
10819   },
10820   {
10821     TYPE_STRING,
10822     &setup.internal.program_company,            "program_company"
10823   },
10824   {
10825     TYPE_STRING,
10826     &setup.internal.program_icon_file,          "program_icon_file"
10827   },
10828   {
10829     TYPE_STRING,
10830     &setup.internal.default_graphics_set,       "default_graphics_set"
10831   },
10832   {
10833     TYPE_STRING,
10834     &setup.internal.default_sounds_set,         "default_sounds_set"
10835   },
10836   {
10837     TYPE_STRING,
10838     &setup.internal.default_music_set,          "default_music_set"
10839   },
10840   {
10841     TYPE_STRING,
10842     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10843   },
10844   {
10845     TYPE_STRING,
10846     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10847   },
10848   {
10849     TYPE_STRING,
10850     &setup.internal.fallback_music_file,        "fallback_music_file"
10851   },
10852   {
10853     TYPE_STRING,
10854     &setup.internal.default_level_series,       "default_level_series"
10855   },
10856   {
10857     TYPE_INTEGER,
10858     &setup.internal.default_window_width,       "default_window_width"
10859   },
10860   {
10861     TYPE_INTEGER,
10862     &setup.internal.default_window_height,      "default_window_height"
10863   },
10864   {
10865     TYPE_BOOLEAN,
10866     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10867   },
10868   {
10869     TYPE_BOOLEAN,
10870     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10871   },
10872   {
10873     TYPE_BOOLEAN,
10874     &setup.internal.create_user_levelset,       "create_user_levelset"
10875   },
10876   {
10877     TYPE_BOOLEAN,
10878     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10879   },
10880   {
10881     TYPE_BOOLEAN,
10882     &setup.internal.menu_game,                  "menu_game"
10883   },
10884   {
10885     TYPE_BOOLEAN,
10886     &setup.internal.menu_engines,               "menu_engines"
10887   },
10888   {
10889     TYPE_BOOLEAN,
10890     &setup.internal.menu_editor,                "menu_editor"
10891   },
10892   {
10893     TYPE_BOOLEAN,
10894     &setup.internal.menu_graphics,              "menu_graphics"
10895   },
10896   {
10897     TYPE_BOOLEAN,
10898     &setup.internal.menu_sound,                 "menu_sound"
10899   },
10900   {
10901     TYPE_BOOLEAN,
10902     &setup.internal.menu_artwork,               "menu_artwork"
10903   },
10904   {
10905     TYPE_BOOLEAN,
10906     &setup.internal.menu_input,                 "menu_input"
10907   },
10908   {
10909     TYPE_BOOLEAN,
10910     &setup.internal.menu_touch,                 "menu_touch"
10911   },
10912   {
10913     TYPE_BOOLEAN,
10914     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10915   },
10916   {
10917     TYPE_BOOLEAN,
10918     &setup.internal.menu_exit,                  "menu_exit"
10919   },
10920   {
10921     TYPE_BOOLEAN,
10922     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10923   },
10924   {
10925     TYPE_BOOLEAN,
10926     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10927   },
10928   {
10929     TYPE_BOOLEAN,
10930     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10931   },
10932   {
10933     TYPE_BOOLEAN,
10934     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10935   },
10936   {
10937     TYPE_BOOLEAN,
10938     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10939   },
10940   {
10941     TYPE_BOOLEAN,
10942     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10943   },
10944   {
10945     TYPE_BOOLEAN,
10946     &setup.internal.info_title,                 "info_title"
10947   },
10948   {
10949     TYPE_BOOLEAN,
10950     &setup.internal.info_elements,              "info_elements"
10951   },
10952   {
10953     TYPE_BOOLEAN,
10954     &setup.internal.info_music,                 "info_music"
10955   },
10956   {
10957     TYPE_BOOLEAN,
10958     &setup.internal.info_credits,               "info_credits"
10959   },
10960   {
10961     TYPE_BOOLEAN,
10962     &setup.internal.info_program,               "info_program"
10963   },
10964   {
10965     TYPE_BOOLEAN,
10966     &setup.internal.info_version,               "info_version"
10967   },
10968   {
10969     TYPE_BOOLEAN,
10970     &setup.internal.info_levelset,              "info_levelset"
10971   },
10972   {
10973     TYPE_BOOLEAN,
10974     &setup.internal.info_exit,                  "info_exit"
10975   },
10976 };
10977
10978 static struct TokenInfo debug_setup_tokens[] =
10979 {
10980   {
10981     TYPE_INTEGER,
10982     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
10983   },
10984   {
10985     TYPE_INTEGER,
10986     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
10987   },
10988   {
10989     TYPE_INTEGER,
10990     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
10991   },
10992   {
10993     TYPE_INTEGER,
10994     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
10995   },
10996   {
10997     TYPE_INTEGER,
10998     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
10999   },
11000   {
11001     TYPE_INTEGER,
11002     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11003   },
11004   {
11005     TYPE_INTEGER,
11006     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11007   },
11008   {
11009     TYPE_INTEGER,
11010     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11011   },
11012   {
11013     TYPE_INTEGER,
11014     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11015   },
11016   {
11017     TYPE_INTEGER,
11018     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11019   },
11020   {
11021     TYPE_KEY_X11,
11022     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11023   },
11024   {
11025     TYPE_KEY_X11,
11026     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11027   },
11028   {
11029     TYPE_KEY_X11,
11030     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11031   },
11032   {
11033     TYPE_KEY_X11,
11034     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11035   },
11036   {
11037     TYPE_KEY_X11,
11038     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11039   },
11040   {
11041     TYPE_KEY_X11,
11042     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11043   },
11044   {
11045     TYPE_KEY_X11,
11046     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11047   },
11048   {
11049     TYPE_KEY_X11,
11050     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11051   },
11052   {
11053     TYPE_KEY_X11,
11054     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11055   },
11056   {
11057     TYPE_KEY_X11,
11058     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11059   },
11060   {
11061     TYPE_BOOLEAN,
11062     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11063   {
11064     TYPE_BOOLEAN,
11065     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11066   },
11067   {
11068     TYPE_BOOLEAN,
11069     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11070   },
11071   {
11072     TYPE_SWITCH3,
11073     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11074   },
11075   {
11076     TYPE_INTEGER,
11077     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11078   },
11079 };
11080
11081 static struct TokenInfo options_setup_tokens[] =
11082 {
11083   {
11084     TYPE_BOOLEAN,
11085     &setup.options.verbose,                     "options.verbose"
11086   },
11087   {
11088     TYPE_BOOLEAN,
11089     &setup.options.debug,                       "options.debug"
11090   },
11091   {
11092     TYPE_STRING,
11093     &setup.options.debug_mode,                  "options.debug_mode"
11094   },
11095 };
11096
11097 static void setSetupInfoToDefaults(struct SetupInfo *si)
11098 {
11099   int i;
11100
11101   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11102
11103   si->multiple_users = TRUE;
11104
11105   si->sound = TRUE;
11106   si->sound_loops = TRUE;
11107   si->sound_music = TRUE;
11108   si->sound_simple = TRUE;
11109   si->toons = TRUE;
11110   si->global_animations = TRUE;
11111   si->scroll_delay = TRUE;
11112   si->forced_scroll_delay = FALSE;
11113   si->scroll_delay_value = STD_SCROLL_DELAY;
11114   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11115   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11116   si->fade_screens = TRUE;
11117   si->autorecord = TRUE;
11118   si->autorecord_after_replay = TRUE;
11119   si->auto_pause_on_start = FALSE;
11120   si->show_titlescreen = TRUE;
11121   si->quick_doors = FALSE;
11122   si->team_mode = FALSE;
11123   si->handicap = TRUE;
11124   si->skip_levels = TRUE;
11125   si->increment_levels = TRUE;
11126   si->auto_play_next_level = TRUE;
11127   si->count_score_after_game = TRUE;
11128   si->show_scores_after_game = TRUE;
11129   si->time_limit = TRUE;
11130   si->fullscreen = FALSE;
11131   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11132   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11133   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11134   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11135   si->ask_on_escape = TRUE;
11136   si->ask_on_escape_editor = TRUE;
11137   si->ask_on_game_over = TRUE;
11138   si->ask_on_quit_game = TRUE;
11139   si->ask_on_quit_program = TRUE;
11140   si->quick_switch = FALSE;
11141   si->input_on_focus = FALSE;
11142   si->prefer_aga_graphics = TRUE;
11143   si->prefer_lowpass_sounds = FALSE;
11144   si->prefer_extra_panel_items = TRUE;
11145   si->game_speed_extended = FALSE;
11146   si->game_frame_delay = GAME_FRAME_DELAY;
11147   si->bd_skip_uncovering = FALSE;
11148   si->bd_skip_hatching = FALSE;
11149   si->bd_scroll_delay = TRUE;
11150   si->bd_smooth_movements = AUTO;
11151   si->sp_show_border_elements = FALSE;
11152   si->small_game_graphics = FALSE;
11153   si->show_load_save_buttons = FALSE;
11154   si->show_undo_redo_buttons = FALSE;
11155   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11156
11157   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11158   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11159   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11160
11161   si->override_level_graphics = FALSE;
11162   si->override_level_sounds = FALSE;
11163   si->override_level_music = FALSE;
11164
11165   si->volume_simple = 100;              // percent
11166   si->volume_loops = 100;               // percent
11167   si->volume_music = 100;               // percent
11168
11169   si->network_mode = FALSE;
11170   si->network_player_nr = 0;            // first player
11171   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11172
11173   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11174   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11175   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11176   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11177   si->touch.draw_outlined = TRUE;
11178   si->touch.draw_pressed = TRUE;
11179
11180   for (i = 0; i < 2; i++)
11181   {
11182     char *default_grid_button[6][2] =
11183     {
11184       { "      ", "  ^^  " },
11185       { "      ", "  ^^  " },
11186       { "      ", "<<  >>" },
11187       { "      ", "<<  >>" },
11188       { "111222", "  vv  " },
11189       { "111222", "  vv  " }
11190     };
11191     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11192     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11193     int min_xsize = MIN(6, grid_xsize);
11194     int min_ysize = MIN(6, grid_ysize);
11195     int startx = grid_xsize - min_xsize;
11196     int starty = grid_ysize - min_ysize;
11197     int x, y;
11198
11199     // virtual buttons grid can only be set to defaults if video is initialized
11200     // (this will be repeated if virtual buttons are not loaded from setup file)
11201     if (video.initialized)
11202     {
11203       si->touch.grid_xsize[i] = grid_xsize;
11204       si->touch.grid_ysize[i] = grid_ysize;
11205     }
11206     else
11207     {
11208       si->touch.grid_xsize[i] = -1;
11209       si->touch.grid_ysize[i] = -1;
11210     }
11211
11212     for (x = 0; x < MAX_GRID_XSIZE; x++)
11213       for (y = 0; y < MAX_GRID_YSIZE; y++)
11214         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11215
11216     for (x = 0; x < min_xsize; x++)
11217       for (y = 0; y < min_ysize; y++)
11218         si->touch.grid_button[i][x][starty + y] =
11219           default_grid_button[y][0][x];
11220
11221     for (x = 0; x < min_xsize; x++)
11222       for (y = 0; y < min_ysize; y++)
11223         si->touch.grid_button[i][startx + x][starty + y] =
11224           default_grid_button[y][1][x];
11225   }
11226
11227   si->touch.grid_initialized            = video.initialized;
11228
11229   si->touch.overlay_buttons             = FALSE;
11230
11231   si->editor.el_boulderdash             = TRUE;
11232   si->editor.el_boulderdash_native      = TRUE;
11233   si->editor.el_boulderdash_effects     = TRUE;
11234   si->editor.el_emerald_mine            = TRUE;
11235   si->editor.el_emerald_mine_club       = TRUE;
11236   si->editor.el_more                    = TRUE;
11237   si->editor.el_sokoban                 = TRUE;
11238   si->editor.el_supaplex                = TRUE;
11239   si->editor.el_diamond_caves           = TRUE;
11240   si->editor.el_dx_boulderdash          = TRUE;
11241
11242   si->editor.el_mirror_magic            = TRUE;
11243   si->editor.el_deflektor               = TRUE;
11244
11245   si->editor.el_chars                   = TRUE;
11246   si->editor.el_steel_chars             = TRUE;
11247
11248   si->editor.el_classic                 = TRUE;
11249   si->editor.el_custom                  = TRUE;
11250
11251   si->editor.el_user_defined            = FALSE;
11252   si->editor.el_dynamic                 = TRUE;
11253
11254   si->editor.el_headlines               = TRUE;
11255
11256   si->editor.show_element_token         = FALSE;
11257
11258   si->editor.show_read_only_warning     = TRUE;
11259
11260   si->editor.use_template_for_new_levels = TRUE;
11261
11262   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11263   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11264   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11265   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11266   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11267
11268   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11269   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11270   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11271   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11272   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11273
11274   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11275   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11276   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11277   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11278   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11279   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11280
11281   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11282   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11283   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11284
11285   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11286   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11287   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11288   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11289
11290   for (i = 0; i < MAX_PLAYERS; i++)
11291   {
11292     si->input[i].use_joystick = FALSE;
11293     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11294     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11295     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11296     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11297     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11298     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11299     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11300     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11301     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11302     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11303     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11304     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11305     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11306     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11307     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11308   }
11309
11310   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11311   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11312   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11313   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11314
11315   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11316   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11317   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11318   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11319   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11320   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11321   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11322
11323   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11324
11325   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11326   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11327   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11328
11329   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11330   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11331   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11332
11333   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11334   si->internal.choose_from_top_leveldir = FALSE;
11335   si->internal.show_scaling_in_title = TRUE;
11336   si->internal.create_user_levelset = TRUE;
11337   si->internal.info_screens_from_main = FALSE;
11338
11339   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11340   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11341
11342   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11343   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11344   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11345   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11346   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11347   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11348   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11349   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11350   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11351   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11352
11353   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11354   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11355   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11356   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11357   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11358   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11359   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11360   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11361   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11362   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11363
11364   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11365   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11366
11367   si->debug.show_frames_per_second = FALSE;
11368
11369   si->debug.xsn_mode = AUTO;
11370   si->debug.xsn_percent = 0;
11371
11372   si->options.verbose = FALSE;
11373   si->options.debug = FALSE;
11374   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11375
11376 #if defined(PLATFORM_ANDROID)
11377   si->fullscreen = TRUE;
11378   si->touch.overlay_buttons = TRUE;
11379 #endif
11380
11381   setHideSetupEntry(&setup.debug.xsn_mode);
11382 }
11383
11384 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11385 {
11386   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11387 }
11388
11389 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11390 {
11391   si->player_uuid = NULL;       // (will be set later)
11392   si->player_version = 1;       // (will be set later)
11393
11394   si->use_api_server = TRUE;
11395   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11396   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11397   si->ask_for_uploading_tapes = TRUE;
11398   si->ask_for_remaining_tapes = FALSE;
11399   si->provide_uploading_tapes = TRUE;
11400   si->ask_for_using_api_server = TRUE;
11401   si->has_remaining_tapes = FALSE;
11402 }
11403
11404 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11405 {
11406   si->editor_cascade.el_bd              = TRUE;
11407   si->editor_cascade.el_bd_native       = TRUE;
11408   si->editor_cascade.el_bd_effects      = FALSE;
11409   si->editor_cascade.el_em              = TRUE;
11410   si->editor_cascade.el_emc             = TRUE;
11411   si->editor_cascade.el_rnd             = TRUE;
11412   si->editor_cascade.el_sb              = TRUE;
11413   si->editor_cascade.el_sp              = TRUE;
11414   si->editor_cascade.el_dc              = TRUE;
11415   si->editor_cascade.el_dx              = TRUE;
11416
11417   si->editor_cascade.el_mm              = TRUE;
11418   si->editor_cascade.el_df              = TRUE;
11419
11420   si->editor_cascade.el_chars           = FALSE;
11421   si->editor_cascade.el_steel_chars     = FALSE;
11422   si->editor_cascade.el_ce              = FALSE;
11423   si->editor_cascade.el_ge              = FALSE;
11424   si->editor_cascade.el_es              = FALSE;
11425   si->editor_cascade.el_ref             = FALSE;
11426   si->editor_cascade.el_user            = FALSE;
11427   si->editor_cascade.el_dynamic         = FALSE;
11428 }
11429
11430 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11431
11432 static char *getHideSetupToken(void *setup_value)
11433 {
11434   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11435
11436   if (setup_value != NULL)
11437     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11438
11439   return hide_setup_token;
11440 }
11441
11442 void setHideSetupEntry(void *setup_value)
11443 {
11444   char *hide_setup_token = getHideSetupToken(setup_value);
11445
11446   if (hide_setup_hash == NULL)
11447     hide_setup_hash = newSetupFileHash();
11448
11449   if (setup_value != NULL)
11450     setHashEntry(hide_setup_hash, hide_setup_token, "");
11451 }
11452
11453 void removeHideSetupEntry(void *setup_value)
11454 {
11455   char *hide_setup_token = getHideSetupToken(setup_value);
11456
11457   if (setup_value != NULL)
11458     removeHashEntry(hide_setup_hash, hide_setup_token);
11459 }
11460
11461 boolean hideSetupEntry(void *setup_value)
11462 {
11463   char *hide_setup_token = getHideSetupToken(setup_value);
11464
11465   return (setup_value != NULL &&
11466           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11467 }
11468
11469 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11470                                       struct TokenInfo *token_info,
11471                                       int token_nr, char *token_text)
11472 {
11473   char *token_hide_text = getStringCat2(token_text, ".hide");
11474   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11475
11476   // set the value of this setup option in the setup option structure
11477   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11478
11479   // check if this setup option should be hidden in the setup menu
11480   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11481     setHideSetupEntry(token_info[token_nr].value);
11482
11483   free(token_hide_text);
11484 }
11485
11486 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11487                                       struct TokenInfo *token_info,
11488                                       int token_nr)
11489 {
11490   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11491                             token_info[token_nr].text);
11492 }
11493
11494 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11495 {
11496   int i, pnr;
11497
11498   if (!setup_file_hash)
11499     return;
11500
11501   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11502     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11503
11504   setup.touch.grid_initialized = TRUE;
11505   for (i = 0; i < 2; i++)
11506   {
11507     int grid_xsize = setup.touch.grid_xsize[i];
11508     int grid_ysize = setup.touch.grid_ysize[i];
11509     int x, y;
11510
11511     // if virtual buttons are not loaded from setup file, repeat initializing
11512     // virtual buttons grid with default values later when video is initialized
11513     if (grid_xsize == -1 ||
11514         grid_ysize == -1)
11515     {
11516       setup.touch.grid_initialized = FALSE;
11517
11518       continue;
11519     }
11520
11521     for (y = 0; y < grid_ysize; y++)
11522     {
11523       char token_string[MAX_LINE_LEN];
11524
11525       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11526
11527       char *value_string = getHashEntry(setup_file_hash, token_string);
11528
11529       if (value_string == NULL)
11530         continue;
11531
11532       for (x = 0; x < grid_xsize; x++)
11533       {
11534         char c = value_string[x];
11535
11536         setup.touch.grid_button[i][x][y] =
11537           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11538       }
11539     }
11540   }
11541
11542   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11543     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11544
11545   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11546     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11547
11548   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11549   {
11550     char prefix[30];
11551
11552     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11553
11554     setup_input = setup.input[pnr];
11555     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11556     {
11557       char full_token[100];
11558
11559       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11560       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11561                                 full_token);
11562     }
11563     setup.input[pnr] = setup_input;
11564   }
11565
11566   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11567     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11568
11569   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11570     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11571
11572   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11573     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11574
11575   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11576     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11577
11578   setHideRelatedSetupEntries();
11579 }
11580
11581 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11582 {
11583   int i;
11584
11585   if (!setup_file_hash)
11586     return;
11587
11588   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11589     setSetupInfo(auto_setup_tokens, i,
11590                  getHashEntry(setup_file_hash,
11591                               auto_setup_tokens[i].text));
11592 }
11593
11594 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11595 {
11596   int i;
11597
11598   if (!setup_file_hash)
11599     return;
11600
11601   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11602     setSetupInfo(server_setup_tokens, i,
11603                  getHashEntry(setup_file_hash,
11604                               server_setup_tokens[i].text));
11605 }
11606
11607 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11608 {
11609   int i;
11610
11611   if (!setup_file_hash)
11612     return;
11613
11614   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11615     setSetupInfo(editor_cascade_setup_tokens, i,
11616                  getHashEntry(setup_file_hash,
11617                               editor_cascade_setup_tokens[i].text));
11618 }
11619
11620 void LoadUserNames(void)
11621 {
11622   int last_user_nr = user.nr;
11623   int i;
11624
11625   if (global.user_names != NULL)
11626   {
11627     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11628       checked_free(global.user_names[i]);
11629
11630     checked_free(global.user_names);
11631   }
11632
11633   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11634
11635   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11636   {
11637     user.nr = i;
11638
11639     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11640
11641     if (setup_file_hash)
11642     {
11643       char *player_name = getHashEntry(setup_file_hash, "player_name");
11644
11645       global.user_names[i] = getFixedUserName(player_name);
11646
11647       freeSetupFileHash(setup_file_hash);
11648     }
11649
11650     if (global.user_names[i] == NULL)
11651       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11652   }
11653
11654   user.nr = last_user_nr;
11655 }
11656
11657 void LoadSetupFromFilename(char *filename)
11658 {
11659   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11660
11661   if (setup_file_hash)
11662   {
11663     decodeSetupFileHash_Default(setup_file_hash);
11664
11665     freeSetupFileHash(setup_file_hash);
11666   }
11667   else
11668   {
11669     Debug("setup", "using default setup values");
11670   }
11671 }
11672
11673 static void LoadSetup_SpecialPostProcessing(void)
11674 {
11675   char *player_name_new;
11676
11677   // needed to work around problems with fixed length strings
11678   player_name_new = getFixedUserName(setup.player_name);
11679   free(setup.player_name);
11680   setup.player_name = player_name_new;
11681
11682   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11683   if (setup.scroll_delay == FALSE)
11684   {
11685     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11686     setup.scroll_delay = TRUE;                  // now always "on"
11687   }
11688
11689   // make sure that scroll delay value stays inside valid range
11690   setup.scroll_delay_value =
11691     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11692 }
11693
11694 void LoadSetup_Default(void)
11695 {
11696   char *filename;
11697
11698   // always start with reliable default values
11699   setSetupInfoToDefaults(&setup);
11700
11701   // try to load setup values from default setup file
11702   filename = getDefaultSetupFilename();
11703
11704   if (fileExists(filename))
11705     LoadSetupFromFilename(filename);
11706
11707   // try to load setup values from platform setup file
11708   filename = getPlatformSetupFilename();
11709
11710   if (fileExists(filename))
11711     LoadSetupFromFilename(filename);
11712
11713   // try to load setup values from user setup file
11714   filename = getSetupFilename();
11715
11716   LoadSetupFromFilename(filename);
11717
11718   LoadSetup_SpecialPostProcessing();
11719 }
11720
11721 void LoadSetup_AutoSetup(void)
11722 {
11723   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11724   SetupFileHash *setup_file_hash = NULL;
11725
11726   // always start with reliable default values
11727   setSetupInfoToDefaults_AutoSetup(&setup);
11728
11729   setup_file_hash = loadSetupFileHash(filename);
11730
11731   if (setup_file_hash)
11732   {
11733     decodeSetupFileHash_AutoSetup(setup_file_hash);
11734
11735     freeSetupFileHash(setup_file_hash);
11736   }
11737
11738   free(filename);
11739 }
11740
11741 void LoadSetup_ServerSetup(void)
11742 {
11743   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11744   SetupFileHash *setup_file_hash = NULL;
11745
11746   // always start with reliable default values
11747   setSetupInfoToDefaults_ServerSetup(&setup);
11748
11749   setup_file_hash = loadSetupFileHash(filename);
11750
11751   if (setup_file_hash)
11752   {
11753     decodeSetupFileHash_ServerSetup(setup_file_hash);
11754
11755     freeSetupFileHash(setup_file_hash);
11756   }
11757
11758   free(filename);
11759
11760   if (setup.player_uuid == NULL)
11761   {
11762     // player UUID does not yet exist in setup file
11763     setup.player_uuid = getStringCopy(getUUID());
11764     setup.player_version = 2;
11765
11766     SaveSetup_ServerSetup();
11767   }
11768 }
11769
11770 void LoadSetup_EditorCascade(void)
11771 {
11772   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11773   SetupFileHash *setup_file_hash = NULL;
11774
11775   // always start with reliable default values
11776   setSetupInfoToDefaults_EditorCascade(&setup);
11777
11778   setup_file_hash = loadSetupFileHash(filename);
11779
11780   if (setup_file_hash)
11781   {
11782     decodeSetupFileHash_EditorCascade(setup_file_hash);
11783
11784     freeSetupFileHash(setup_file_hash);
11785   }
11786
11787   free(filename);
11788 }
11789
11790 void LoadSetup(void)
11791 {
11792   LoadSetup_Default();
11793   LoadSetup_AutoSetup();
11794   LoadSetup_ServerSetup();
11795   LoadSetup_EditorCascade();
11796 }
11797
11798 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11799                                            char *mapping_line)
11800 {
11801   char mapping_guid[MAX_LINE_LEN];
11802   char *mapping_start, *mapping_end;
11803
11804   // get GUID from game controller mapping line: copy complete line
11805   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11806   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11807
11808   // get GUID from game controller mapping line: cut after GUID part
11809   mapping_start = strchr(mapping_guid, ',');
11810   if (mapping_start != NULL)
11811     *mapping_start = '\0';
11812
11813   // cut newline from game controller mapping line
11814   mapping_end = strchr(mapping_line, '\n');
11815   if (mapping_end != NULL)
11816     *mapping_end = '\0';
11817
11818   // add mapping entry to game controller mappings hash
11819   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11820 }
11821
11822 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11823                                                  char *filename)
11824 {
11825   FILE *file;
11826
11827   if (!(file = fopen(filename, MODE_READ)))
11828   {
11829     Warn("cannot read game controller mappings file '%s'", filename);
11830
11831     return;
11832   }
11833
11834   while (!feof(file))
11835   {
11836     char line[MAX_LINE_LEN];
11837
11838     if (!fgets(line, MAX_LINE_LEN, file))
11839       break;
11840
11841     addGameControllerMappingToHash(mappings_hash, line);
11842   }
11843
11844   fclose(file);
11845 }
11846
11847 void SaveSetup_Default(void)
11848 {
11849   char *filename = getSetupFilename();
11850   FILE *file;
11851   int i, pnr;
11852
11853   InitUserDataDirectory();
11854
11855   if (!(file = fopen(filename, MODE_WRITE)))
11856   {
11857     Warn("cannot write setup file '%s'", filename);
11858
11859     return;
11860   }
11861
11862   fprintFileHeader(file, SETUP_FILENAME);
11863
11864   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11865   {
11866     // just to make things nicer :)
11867     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11868         global_setup_tokens[i].value == &setup.sound                    ||
11869         global_setup_tokens[i].value == &setup.graphics_set             ||
11870         global_setup_tokens[i].value == &setup.volume_simple            ||
11871         global_setup_tokens[i].value == &setup.network_mode             ||
11872         global_setup_tokens[i].value == &setup.touch.control_type       ||
11873         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11874         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11875       fprintf(file, "\n");
11876
11877     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11878   }
11879
11880   for (i = 0; i < 2; i++)
11881   {
11882     int grid_xsize = setup.touch.grid_xsize[i];
11883     int grid_ysize = setup.touch.grid_ysize[i];
11884     int x, y;
11885
11886     fprintf(file, "\n");
11887
11888     for (y = 0; y < grid_ysize; y++)
11889     {
11890       char token_string[MAX_LINE_LEN];
11891       char value_string[MAX_LINE_LEN];
11892
11893       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11894
11895       for (x = 0; x < grid_xsize; x++)
11896       {
11897         char c = setup.touch.grid_button[i][x][y];
11898
11899         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11900       }
11901
11902       value_string[grid_xsize] = '\0';
11903
11904       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11905     }
11906   }
11907
11908   fprintf(file, "\n");
11909   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11910     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11911
11912   fprintf(file, "\n");
11913   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11914     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11915
11916   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11917   {
11918     char prefix[30];
11919
11920     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11921     fprintf(file, "\n");
11922
11923     setup_input = setup.input[pnr];
11924     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11925       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11926   }
11927
11928   fprintf(file, "\n");
11929   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11930     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11931
11932   // (internal setup values not saved to user setup file)
11933
11934   fprintf(file, "\n");
11935   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11936     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11937         setup.debug.xsn_mode != AUTO)
11938       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11939
11940   fprintf(file, "\n");
11941   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11942     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11943
11944   fclose(file);
11945
11946   SetFilePermissions(filename, PERMS_PRIVATE);
11947 }
11948
11949 void SaveSetup_AutoSetup(void)
11950 {
11951   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11952   FILE *file;
11953   int i;
11954
11955   InitUserDataDirectory();
11956
11957   if (!(file = fopen(filename, MODE_WRITE)))
11958   {
11959     Warn("cannot write auto setup file '%s'", filename);
11960
11961     free(filename);
11962
11963     return;
11964   }
11965
11966   fprintFileHeader(file, AUTOSETUP_FILENAME);
11967
11968   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11969     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11970
11971   fclose(file);
11972
11973   SetFilePermissions(filename, PERMS_PRIVATE);
11974
11975   free(filename);
11976 }
11977
11978 void SaveSetup_ServerSetup(void)
11979 {
11980   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11981   FILE *file;
11982   int i;
11983
11984   InitUserDataDirectory();
11985
11986   if (!(file = fopen(filename, MODE_WRITE)))
11987   {
11988     Warn("cannot write server setup file '%s'", filename);
11989
11990     free(filename);
11991
11992     return;
11993   }
11994
11995   fprintFileHeader(file, SERVERSETUP_FILENAME);
11996
11997   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11998   {
11999     // just to make things nicer :)
12000     if (server_setup_tokens[i].value == &setup.use_api_server)
12001       fprintf(file, "\n");
12002
12003     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12004   }
12005
12006   fclose(file);
12007
12008   SetFilePermissions(filename, PERMS_PRIVATE);
12009
12010   free(filename);
12011 }
12012
12013 void SaveSetup_EditorCascade(void)
12014 {
12015   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12016   FILE *file;
12017   int i;
12018
12019   InitUserDataDirectory();
12020
12021   if (!(file = fopen(filename, MODE_WRITE)))
12022   {
12023     Warn("cannot write editor cascade state file '%s'", filename);
12024
12025     free(filename);
12026
12027     return;
12028   }
12029
12030   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12031
12032   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12033     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12034
12035   fclose(file);
12036
12037   SetFilePermissions(filename, PERMS_PRIVATE);
12038
12039   free(filename);
12040 }
12041
12042 void SaveSetup(void)
12043 {
12044   SaveSetup_Default();
12045   SaveSetup_AutoSetup();
12046   SaveSetup_ServerSetup();
12047   SaveSetup_EditorCascade();
12048 }
12049
12050 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12051                                                   char *filename)
12052 {
12053   FILE *file;
12054
12055   if (!(file = fopen(filename, MODE_WRITE)))
12056   {
12057     Warn("cannot write game controller mappings file '%s'", filename);
12058
12059     return;
12060   }
12061
12062   BEGIN_HASH_ITERATION(mappings_hash, itr)
12063   {
12064     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12065   }
12066   END_HASH_ITERATION(mappings_hash, itr)
12067
12068   fclose(file);
12069 }
12070
12071 void SaveSetup_AddGameControllerMapping(char *mapping)
12072 {
12073   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12074   SetupFileHash *mappings_hash = newSetupFileHash();
12075
12076   InitUserDataDirectory();
12077
12078   // load existing personal game controller mappings
12079   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12080
12081   // add new mapping to personal game controller mappings
12082   addGameControllerMappingToHash(mappings_hash, mapping);
12083
12084   // save updated personal game controller mappings
12085   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12086
12087   freeSetupFileHash(mappings_hash);
12088   free(filename);
12089 }
12090
12091 void LoadCustomElementDescriptions(void)
12092 {
12093   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12094   SetupFileHash *setup_file_hash;
12095   int i;
12096
12097   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12098   {
12099     if (element_info[i].custom_description != NULL)
12100     {
12101       free(element_info[i].custom_description);
12102       element_info[i].custom_description = NULL;
12103     }
12104   }
12105
12106   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12107     return;
12108
12109   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12110   {
12111     char *token = getStringCat2(element_info[i].token_name, ".name");
12112     char *value = getHashEntry(setup_file_hash, token);
12113
12114     if (value != NULL)
12115       element_info[i].custom_description = getStringCopy(value);
12116
12117     free(token);
12118   }
12119
12120   freeSetupFileHash(setup_file_hash);
12121 }
12122
12123 static int getElementFromToken(char *token)
12124 {
12125   char *value = getHashEntry(element_token_hash, token);
12126
12127   if (value != NULL)
12128     return atoi(value);
12129
12130   Warn("unknown element token '%s'", token);
12131
12132   return EL_UNDEFINED;
12133 }
12134
12135 void FreeGlobalAnimEventInfo(void)
12136 {
12137   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12138
12139   if (gaei->event_list == NULL)
12140     return;
12141
12142   int i;
12143
12144   for (i = 0; i < gaei->num_event_lists; i++)
12145   {
12146     checked_free(gaei->event_list[i]->event_value);
12147     checked_free(gaei->event_list[i]);
12148   }
12149
12150   checked_free(gaei->event_list);
12151
12152   gaei->event_list = NULL;
12153   gaei->num_event_lists = 0;
12154 }
12155
12156 static int AddGlobalAnimEventList(void)
12157 {
12158   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12159   int list_pos = gaei->num_event_lists++;
12160
12161   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12162                                      sizeof(struct GlobalAnimEventListInfo *));
12163
12164   gaei->event_list[list_pos] =
12165     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12166
12167   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12168
12169   gaeli->event_value = NULL;
12170   gaeli->num_event_values = 0;
12171
12172   return list_pos;
12173 }
12174
12175 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12176 {
12177   // do not add empty global animation events
12178   if (event_value == ANIM_EVENT_NONE)
12179     return list_pos;
12180
12181   // if list position is undefined, create new list
12182   if (list_pos == ANIM_EVENT_UNDEFINED)
12183     list_pos = AddGlobalAnimEventList();
12184
12185   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12186   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12187   int value_pos = gaeli->num_event_values++;
12188
12189   gaeli->event_value = checked_realloc(gaeli->event_value,
12190                                        gaeli->num_event_values * sizeof(int *));
12191
12192   gaeli->event_value[value_pos] = event_value;
12193
12194   return list_pos;
12195 }
12196
12197 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12198 {
12199   if (list_pos == ANIM_EVENT_UNDEFINED)
12200     return ANIM_EVENT_NONE;
12201
12202   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12203   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12204
12205   return gaeli->event_value[value_pos];
12206 }
12207
12208 int GetGlobalAnimEventValueCount(int list_pos)
12209 {
12210   if (list_pos == ANIM_EVENT_UNDEFINED)
12211     return 0;
12212
12213   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12214   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12215
12216   return gaeli->num_event_values;
12217 }
12218
12219 // This function checks if a string <s> of the format "string1, string2, ..."
12220 // exactly contains a string <s_contained>.
12221
12222 static boolean string_has_parameter(char *s, char *s_contained)
12223 {
12224   char *substring;
12225
12226   if (s == NULL || s_contained == NULL)
12227     return FALSE;
12228
12229   if (strlen(s_contained) > strlen(s))
12230     return FALSE;
12231
12232   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12233   {
12234     char next_char = s[strlen(s_contained)];
12235
12236     // check if next character is delimiter or whitespace
12237     if (next_char == ',' || next_char == '\0' ||
12238         next_char == ' ' || next_char == '\t')
12239       return TRUE;
12240   }
12241
12242   // check if string contains another parameter string after a comma
12243   substring = strchr(s, ',');
12244   if (substring == NULL)        // string does not contain a comma
12245     return FALSE;
12246
12247   // advance string pointer to next character after the comma
12248   substring++;
12249
12250   // skip potential whitespaces after the comma
12251   while (*substring == ' ' || *substring == '\t')
12252     substring++;
12253
12254   return string_has_parameter(substring, s_contained);
12255 }
12256
12257 static int get_anim_parameter_value_ce(char *s)
12258 {
12259   char *s_ptr = s;
12260   char *pattern_1 = "ce_change:custom_";
12261   char *pattern_2 = ".page_";
12262   int pattern_1_len = strlen(pattern_1);
12263   char *matching_char = strstr(s_ptr, pattern_1);
12264   int result = ANIM_EVENT_NONE;
12265
12266   if (matching_char == NULL)
12267     return ANIM_EVENT_NONE;
12268
12269   result = ANIM_EVENT_CE_CHANGE;
12270
12271   s_ptr = matching_char + pattern_1_len;
12272
12273   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12274   if (*s_ptr >= '0' && *s_ptr <= '9')
12275   {
12276     int gic_ce_nr = (*s_ptr++ - '0');
12277
12278     if (*s_ptr >= '0' && *s_ptr <= '9')
12279     {
12280       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12281
12282       if (*s_ptr >= '0' && *s_ptr <= '9')
12283         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12284     }
12285
12286     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12287       return ANIM_EVENT_NONE;
12288
12289     // custom element stored as 0 to 255
12290     gic_ce_nr--;
12291
12292     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12293   }
12294   else
12295   {
12296     // invalid custom element number specified
12297
12298     return ANIM_EVENT_NONE;
12299   }
12300
12301   // check for change page number ("page_X" or "page_XX") (optional)
12302   if (strPrefix(s_ptr, pattern_2))
12303   {
12304     s_ptr += strlen(pattern_2);
12305
12306     if (*s_ptr >= '0' && *s_ptr <= '9')
12307     {
12308       int gic_page_nr = (*s_ptr++ - '0');
12309
12310       if (*s_ptr >= '0' && *s_ptr <= '9')
12311         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12312
12313       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12314         return ANIM_EVENT_NONE;
12315
12316       // change page stored as 1 to 32 (0 means "all change pages")
12317
12318       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12319     }
12320     else
12321     {
12322       // invalid animation part number specified
12323
12324       return ANIM_EVENT_NONE;
12325     }
12326   }
12327
12328   // discard result if next character is neither delimiter nor whitespace
12329   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12330         *s_ptr == ' ' || *s_ptr == '\t'))
12331     return ANIM_EVENT_NONE;
12332
12333   return result;
12334 }
12335
12336 static int get_anim_parameter_value(char *s)
12337 {
12338   int event_value[] =
12339   {
12340     ANIM_EVENT_CLICK,
12341     ANIM_EVENT_INIT,
12342     ANIM_EVENT_START,
12343     ANIM_EVENT_END,
12344     ANIM_EVENT_POST
12345   };
12346   char *pattern_1[] =
12347   {
12348     "click:anim_",
12349     "init:anim_",
12350     "start:anim_",
12351     "end:anim_",
12352     "post:anim_"
12353   };
12354   char *pattern_2 = ".part_";
12355   char *matching_char = NULL;
12356   char *s_ptr = s;
12357   int pattern_1_len = 0;
12358   int result = ANIM_EVENT_NONE;
12359   int i;
12360
12361   result = get_anim_parameter_value_ce(s);
12362
12363   if (result != ANIM_EVENT_NONE)
12364     return result;
12365
12366   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12367   {
12368     matching_char = strstr(s_ptr, pattern_1[i]);
12369     pattern_1_len = strlen(pattern_1[i]);
12370     result = event_value[i];
12371
12372     if (matching_char != NULL)
12373       break;
12374   }
12375
12376   if (matching_char == NULL)
12377     return ANIM_EVENT_NONE;
12378
12379   s_ptr = matching_char + pattern_1_len;
12380
12381   // check for main animation number ("anim_X" or "anim_XX")
12382   if (*s_ptr >= '0' && *s_ptr <= '9')
12383   {
12384     int gic_anim_nr = (*s_ptr++ - '0');
12385
12386     if (*s_ptr >= '0' && *s_ptr <= '9')
12387       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12388
12389     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12390       return ANIM_EVENT_NONE;
12391
12392     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12393   }
12394   else
12395   {
12396     // invalid main animation number specified
12397
12398     return ANIM_EVENT_NONE;
12399   }
12400
12401   // check for animation part number ("part_X" or "part_XX") (optional)
12402   if (strPrefix(s_ptr, pattern_2))
12403   {
12404     s_ptr += strlen(pattern_2);
12405
12406     if (*s_ptr >= '0' && *s_ptr <= '9')
12407     {
12408       int gic_part_nr = (*s_ptr++ - '0');
12409
12410       if (*s_ptr >= '0' && *s_ptr <= '9')
12411         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12412
12413       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12414         return ANIM_EVENT_NONE;
12415
12416       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12417     }
12418     else
12419     {
12420       // invalid animation part number specified
12421
12422       return ANIM_EVENT_NONE;
12423     }
12424   }
12425
12426   // discard result if next character is neither delimiter nor whitespace
12427   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12428         *s_ptr == ' ' || *s_ptr == '\t'))
12429     return ANIM_EVENT_NONE;
12430
12431   return result;
12432 }
12433
12434 static int get_anim_parameter_values(char *s)
12435 {
12436   int list_pos = ANIM_EVENT_UNDEFINED;
12437   int event_value = ANIM_EVENT_DEFAULT;
12438
12439   if (string_has_parameter(s, "any"))
12440     event_value |= ANIM_EVENT_ANY;
12441
12442   if (string_has_parameter(s, "click:self") ||
12443       string_has_parameter(s, "click") ||
12444       string_has_parameter(s, "self"))
12445     event_value |= ANIM_EVENT_SELF;
12446
12447   if (string_has_parameter(s, "unclick:any"))
12448     event_value |= ANIM_EVENT_UNCLICK_ANY;
12449
12450   // if animation event found, add it to global animation event list
12451   if (event_value != ANIM_EVENT_NONE)
12452     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12453
12454   while (s != NULL)
12455   {
12456     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12457     event_value = get_anim_parameter_value(s);
12458
12459     // if animation event found, add it to global animation event list
12460     if (event_value != ANIM_EVENT_NONE)
12461       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12462
12463     // continue with next part of the string, starting with next comma
12464     s = strchr(s + 1, ',');
12465   }
12466
12467   return list_pos;
12468 }
12469
12470 static int get_anim_action_parameter_value(char *token)
12471 {
12472   // check most common default case first to massively speed things up
12473   if (strEqual(token, ARG_UNDEFINED))
12474     return ANIM_EVENT_ACTION_NONE;
12475
12476   int result = getImageIDFromToken(token);
12477
12478   if (result == -1)
12479   {
12480     char *gfx_token = getStringCat2("gfx.", token);
12481
12482     result = getImageIDFromToken(gfx_token);
12483
12484     checked_free(gfx_token);
12485   }
12486
12487   if (result == -1)
12488   {
12489     Key key = getKeyFromX11KeyName(token);
12490
12491     if (key != KSYM_UNDEFINED)
12492       result = -(int)key;
12493   }
12494
12495   if (result == -1)
12496   {
12497     if (isURL(token))
12498     {
12499       result = get_hash_from_string(token);     // unsigned int => int
12500       result = ABS(result);                     // may be negative now
12501       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12502
12503       setHashEntry(anim_url_hash, int2str(result, 0), token);
12504     }
12505   }
12506
12507   if (result == -1)
12508     result = ANIM_EVENT_ACTION_NONE;
12509
12510   return result;
12511 }
12512
12513 int get_parameter_value(char *value_raw, char *suffix, int type)
12514 {
12515   char *value = getStringToLower(value_raw);
12516   int result = 0;       // probably a save default value
12517
12518   if (strEqual(suffix, ".direction"))
12519   {
12520     result = (strEqual(value, "left")  ? MV_LEFT :
12521               strEqual(value, "right") ? MV_RIGHT :
12522               strEqual(value, "up")    ? MV_UP :
12523               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12524   }
12525   else if (strEqual(suffix, ".position"))
12526   {
12527     result = (strEqual(value, "left")   ? POS_LEFT :
12528               strEqual(value, "right")  ? POS_RIGHT :
12529               strEqual(value, "top")    ? POS_TOP :
12530               strEqual(value, "upper")  ? POS_UPPER :
12531               strEqual(value, "middle") ? POS_MIDDLE :
12532               strEqual(value, "lower")  ? POS_LOWER :
12533               strEqual(value, "bottom") ? POS_BOTTOM :
12534               strEqual(value, "any")    ? POS_ANY :
12535               strEqual(value, "ce")     ? POS_CE :
12536               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12537               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12538   }
12539   else if (strEqual(suffix, ".align"))
12540   {
12541     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12542               strEqual(value, "right")  ? ALIGN_RIGHT :
12543               strEqual(value, "center") ? ALIGN_CENTER :
12544               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12545   }
12546   else if (strEqual(suffix, ".valign"))
12547   {
12548     result = (strEqual(value, "top")    ? VALIGN_TOP :
12549               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12550               strEqual(value, "middle") ? VALIGN_MIDDLE :
12551               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12552   }
12553   else if (strEqual(suffix, ".anim_mode"))
12554   {
12555     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12556               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12557               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12558               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12559               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12560               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12561               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12562               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12563               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12564               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12565               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12566               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12567               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12568               string_has_parameter(value, "all")        ? ANIM_ALL :
12569               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12570               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12571               ANIM_DEFAULT);
12572
12573     if (string_has_parameter(value, "once"))
12574       result |= ANIM_ONCE;
12575
12576     if (string_has_parameter(value, "reverse"))
12577       result |= ANIM_REVERSE;
12578
12579     if (string_has_parameter(value, "opaque_player"))
12580       result |= ANIM_OPAQUE_PLAYER;
12581
12582     if (string_has_parameter(value, "static_panel"))
12583       result |= ANIM_STATIC_PANEL;
12584   }
12585   else if (strEqual(suffix, ".init_event") ||
12586            strEqual(suffix, ".anim_event"))
12587   {
12588     result = get_anim_parameter_values(value);
12589   }
12590   else if (strEqual(suffix, ".init_delay_action") ||
12591            strEqual(suffix, ".anim_delay_action") ||
12592            strEqual(suffix, ".post_delay_action") ||
12593            strEqual(suffix, ".init_event_action") ||
12594            strEqual(suffix, ".anim_event_action"))
12595   {
12596     result = get_anim_action_parameter_value(value_raw);
12597   }
12598   else if (strEqual(suffix, ".class"))
12599   {
12600     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12601               get_hash_from_string(value));
12602   }
12603   else if (strEqual(suffix, ".style"))
12604   {
12605     result = STYLE_DEFAULT;
12606
12607     if (string_has_parameter(value, "accurate_borders"))
12608       result |= STYLE_ACCURATE_BORDERS;
12609
12610     if (string_has_parameter(value, "inner_corners"))
12611       result |= STYLE_INNER_CORNERS;
12612
12613     if (string_has_parameter(value, "reverse"))
12614       result |= STYLE_REVERSE;
12615
12616     if (string_has_parameter(value, "leftmost_position"))
12617       result |= STYLE_LEFTMOST_POSITION;
12618
12619     if (string_has_parameter(value, "block_clicks"))
12620       result |= STYLE_BLOCK;
12621
12622     if (string_has_parameter(value, "passthrough_clicks"))
12623       result |= STYLE_PASSTHROUGH;
12624
12625     if (string_has_parameter(value, "multiple_actions"))
12626       result |= STYLE_MULTIPLE_ACTIONS;
12627
12628     if (string_has_parameter(value, "consume_ce_event"))
12629       result |= STYLE_CONSUME_CE_EVENT;
12630   }
12631   else if (strEqual(suffix, ".fade_mode"))
12632   {
12633     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12634               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12635               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12636               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12637               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12638               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12639               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12640               FADE_MODE_DEFAULT);
12641   }
12642   else if (strEqual(suffix, ".auto_delay_unit"))
12643   {
12644     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12645               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12646               AUTO_DELAY_UNIT_DEFAULT);
12647   }
12648   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12649   {
12650     result = gfx.get_font_from_token_function(value);
12651   }
12652   else          // generic parameter of type integer or boolean
12653   {
12654     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12655               type == TYPE_INTEGER ? get_integer_from_string(value) :
12656               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12657               ARG_UNDEFINED_VALUE);
12658   }
12659
12660   free(value);
12661
12662   return result;
12663 }
12664
12665 static int get_token_parameter_value(char *token, char *value_raw)
12666 {
12667   char *suffix;
12668
12669   if (token == NULL || value_raw == NULL)
12670     return ARG_UNDEFINED_VALUE;
12671
12672   suffix = strrchr(token, '.');
12673   if (suffix == NULL)
12674     suffix = token;
12675
12676   if (strEqual(suffix, ".element"))
12677     return getElementFromToken(value_raw);
12678
12679   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12680   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12681 }
12682
12683 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12684                                      boolean ignore_defaults)
12685 {
12686   int i;
12687
12688   for (i = 0; image_config_vars[i].token != NULL; i++)
12689   {
12690     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12691
12692     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12693     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12694       continue;
12695
12696     if (value != NULL)
12697       *image_config_vars[i].value =
12698         get_token_parameter_value(image_config_vars[i].token, value);
12699   }
12700 }
12701
12702 void InitMenuDesignSettings_Static(void)
12703 {
12704   // always start with reliable default values from static default config
12705   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12706 }
12707
12708 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12709 {
12710   int i;
12711
12712   // the following initializes hierarchical values from static configuration
12713
12714   // special case: initialize "ARG_DEFAULT" values in static default config
12715   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12716   titlescreen_initial_first_default.fade_mode  =
12717     title_initial_first_default.fade_mode;
12718   titlescreen_initial_first_default.fade_delay =
12719     title_initial_first_default.fade_delay;
12720   titlescreen_initial_first_default.post_delay =
12721     title_initial_first_default.post_delay;
12722   titlescreen_initial_first_default.auto_delay =
12723     title_initial_first_default.auto_delay;
12724   titlescreen_initial_first_default.auto_delay_unit =
12725     title_initial_first_default.auto_delay_unit;
12726   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12727   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12728   titlescreen_first_default.post_delay = title_first_default.post_delay;
12729   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12730   titlescreen_first_default.auto_delay_unit =
12731     title_first_default.auto_delay_unit;
12732   titlemessage_initial_first_default.fade_mode  =
12733     title_initial_first_default.fade_mode;
12734   titlemessage_initial_first_default.fade_delay =
12735     title_initial_first_default.fade_delay;
12736   titlemessage_initial_first_default.post_delay =
12737     title_initial_first_default.post_delay;
12738   titlemessage_initial_first_default.auto_delay =
12739     title_initial_first_default.auto_delay;
12740   titlemessage_initial_first_default.auto_delay_unit =
12741     title_initial_first_default.auto_delay_unit;
12742   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12743   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12744   titlemessage_first_default.post_delay = title_first_default.post_delay;
12745   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12746   titlemessage_first_default.auto_delay_unit =
12747     title_first_default.auto_delay_unit;
12748
12749   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12750   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12751   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12752   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12753   titlescreen_initial_default.auto_delay_unit =
12754     title_initial_default.auto_delay_unit;
12755   titlescreen_default.fade_mode  = title_default.fade_mode;
12756   titlescreen_default.fade_delay = title_default.fade_delay;
12757   titlescreen_default.post_delay = title_default.post_delay;
12758   titlescreen_default.auto_delay = title_default.auto_delay;
12759   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12760   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12761   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12762   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12763   titlemessage_initial_default.auto_delay_unit =
12764     title_initial_default.auto_delay_unit;
12765   titlemessage_default.fade_mode  = title_default.fade_mode;
12766   titlemessage_default.fade_delay = title_default.fade_delay;
12767   titlemessage_default.post_delay = title_default.post_delay;
12768   titlemessage_default.auto_delay = title_default.auto_delay;
12769   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12770
12771   // special case: initialize "ARG_DEFAULT" values in static default config
12772   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12773   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12774   {
12775     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12776     titlescreen_first[i] = titlescreen_first_default;
12777     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12778     titlemessage_first[i] = titlemessage_first_default;
12779
12780     titlescreen_initial[i] = titlescreen_initial_default;
12781     titlescreen[i] = titlescreen_default;
12782     titlemessage_initial[i] = titlemessage_initial_default;
12783     titlemessage[i] = titlemessage_default;
12784   }
12785
12786   // special case: initialize "ARG_DEFAULT" values in static default config
12787   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12788   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12789   {
12790     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12791       continue;
12792
12793     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12794     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12795     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12796   }
12797
12798   // special case: initialize "ARG_DEFAULT" values in static default config
12799   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12800   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12801   {
12802     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12803     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12804     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12805
12806     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12807       continue;
12808
12809     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12810   }
12811 }
12812
12813 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12814 {
12815   static struct
12816   {
12817     struct XY *dst, *src;
12818   }
12819   game_buttons_xy[] =
12820   {
12821     { &game.button.save,        &game.button.stop       },
12822     { &game.button.pause2,      &game.button.pause      },
12823     { &game.button.load,        &game.button.play       },
12824     { &game.button.undo,        &game.button.stop       },
12825     { &game.button.redo,        &game.button.play       },
12826
12827     { NULL,                     NULL                    }
12828   };
12829   int i, j;
12830
12831   // special case: initialize later added SETUP list size from LEVELS value
12832   if (menu.list_size[GAME_MODE_SETUP] == -1)
12833     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12834
12835   // set default position for snapshot buttons to stop/pause/play buttons
12836   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12837     if ((*game_buttons_xy[i].dst).x == -1 &&
12838         (*game_buttons_xy[i].dst).y == -1)
12839       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12840
12841   // --------------------------------------------------------------------------
12842   // dynamic viewports (including playfield margins, borders and alignments)
12843   // --------------------------------------------------------------------------
12844
12845   // dynamic viewports currently only supported for landscape mode
12846   int display_width  = MAX(video.display_width, video.display_height);
12847   int display_height = MIN(video.display_width, video.display_height);
12848
12849   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12850   {
12851     struct RectWithBorder *vp_window    = &viewport.window[i];
12852     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12853     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12854     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12855     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12856     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12857     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12858     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12859
12860     // adjust window size if min/max width/height is specified
12861
12862     if (vp_window->min_width != -1)
12863     {
12864       int window_width = display_width;
12865
12866       // when using static window height, use aspect ratio of display
12867       if (vp_window->min_height == -1)
12868         window_width = vp_window->height * display_width / display_height;
12869
12870       vp_window->width = MAX(vp_window->min_width, window_width);
12871     }
12872
12873     if (vp_window->min_height != -1)
12874     {
12875       int window_height = display_height;
12876
12877       // when using static window width, use aspect ratio of display
12878       if (vp_window->min_width == -1)
12879         window_height = vp_window->width * display_height / display_width;
12880
12881       vp_window->height = MAX(vp_window->min_height, window_height);
12882     }
12883
12884     if (vp_window->max_width != -1)
12885       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12886
12887     if (vp_window->max_height != -1)
12888       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12889
12890     int playfield_width  = vp_window->width;
12891     int playfield_height = vp_window->height;
12892
12893     // adjust playfield size and position according to specified margins
12894
12895     playfield_width  -= vp_playfield->margin_left;
12896     playfield_width  -= vp_playfield->margin_right;
12897
12898     playfield_height -= vp_playfield->margin_top;
12899     playfield_height -= vp_playfield->margin_bottom;
12900
12901     // adjust playfield size if min/max width/height is specified
12902
12903     if (vp_playfield->min_width != -1)
12904       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12905
12906     if (vp_playfield->min_height != -1)
12907       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12908
12909     if (vp_playfield->max_width != -1)
12910       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12911
12912     if (vp_playfield->max_height != -1)
12913       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12914
12915     // adjust playfield position according to specified alignment
12916
12917     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12918       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12919     else if (vp_playfield->align == ALIGN_CENTER)
12920       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12921     else if (vp_playfield->align == ALIGN_RIGHT)
12922       vp_playfield->x += playfield_width - vp_playfield->width;
12923
12924     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12925       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12926     else if (vp_playfield->valign == VALIGN_MIDDLE)
12927       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12928     else if (vp_playfield->valign == VALIGN_BOTTOM)
12929       vp_playfield->y += playfield_height - vp_playfield->height;
12930
12931     vp_playfield->x += vp_playfield->margin_left;
12932     vp_playfield->y += vp_playfield->margin_top;
12933
12934     // adjust individual playfield borders if only default border is specified
12935
12936     if (vp_playfield->border_left == -1)
12937       vp_playfield->border_left = vp_playfield->border_size;
12938     if (vp_playfield->border_right == -1)
12939       vp_playfield->border_right = vp_playfield->border_size;
12940     if (vp_playfield->border_top == -1)
12941       vp_playfield->border_top = vp_playfield->border_size;
12942     if (vp_playfield->border_bottom == -1)
12943       vp_playfield->border_bottom = vp_playfield->border_size;
12944
12945     // set dynamic playfield borders if borders are specified as undefined
12946     // (but only if window size was dynamic and playfield size was static)
12947
12948     if (dynamic_window_width && !dynamic_playfield_width)
12949     {
12950       if (vp_playfield->border_left == -1)
12951       {
12952         vp_playfield->border_left = (vp_playfield->x -
12953                                      vp_playfield->margin_left);
12954         vp_playfield->x     -= vp_playfield->border_left;
12955         vp_playfield->width += vp_playfield->border_left;
12956       }
12957
12958       if (vp_playfield->border_right == -1)
12959       {
12960         vp_playfield->border_right = (vp_window->width -
12961                                       vp_playfield->x -
12962                                       vp_playfield->width -
12963                                       vp_playfield->margin_right);
12964         vp_playfield->width += vp_playfield->border_right;
12965       }
12966     }
12967
12968     if (dynamic_window_height && !dynamic_playfield_height)
12969     {
12970       if (vp_playfield->border_top == -1)
12971       {
12972         vp_playfield->border_top = (vp_playfield->y -
12973                                     vp_playfield->margin_top);
12974         vp_playfield->y      -= vp_playfield->border_top;
12975         vp_playfield->height += vp_playfield->border_top;
12976       }
12977
12978       if (vp_playfield->border_bottom == -1)
12979       {
12980         vp_playfield->border_bottom = (vp_window->height -
12981                                        vp_playfield->y -
12982                                        vp_playfield->height -
12983                                        vp_playfield->margin_bottom);
12984         vp_playfield->height += vp_playfield->border_bottom;
12985       }
12986     }
12987
12988     // adjust playfield size to be a multiple of a defined alignment tile size
12989
12990     int align_size = vp_playfield->align_size;
12991     int playfield_xtiles = vp_playfield->width  / align_size;
12992     int playfield_ytiles = vp_playfield->height / align_size;
12993     int playfield_width_corrected  = playfield_xtiles * align_size;
12994     int playfield_height_corrected = playfield_ytiles * align_size;
12995     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12996                                  i == GFX_SPECIAL_ARG_EDITOR);
12997
12998     if (is_playfield_mode &&
12999         dynamic_playfield_width &&
13000         vp_playfield->width != playfield_width_corrected)
13001     {
13002       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13003
13004       vp_playfield->width = playfield_width_corrected;
13005
13006       if (vp_playfield->align == ALIGN_LEFT)
13007       {
13008         vp_playfield->border_left += playfield_xdiff;
13009       }
13010       else if (vp_playfield->align == ALIGN_RIGHT)
13011       {
13012         vp_playfield->border_right += playfield_xdiff;
13013       }
13014       else if (vp_playfield->align == ALIGN_CENTER)
13015       {
13016         int border_left_diff  = playfield_xdiff / 2;
13017         int border_right_diff = playfield_xdiff - border_left_diff;
13018
13019         vp_playfield->border_left  += border_left_diff;
13020         vp_playfield->border_right += border_right_diff;
13021       }
13022     }
13023
13024     if (is_playfield_mode &&
13025         dynamic_playfield_height &&
13026         vp_playfield->height != playfield_height_corrected)
13027     {
13028       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13029
13030       vp_playfield->height = playfield_height_corrected;
13031
13032       if (vp_playfield->valign == VALIGN_TOP)
13033       {
13034         vp_playfield->border_top += playfield_ydiff;
13035       }
13036       else if (vp_playfield->align == VALIGN_BOTTOM)
13037       {
13038         vp_playfield->border_right += playfield_ydiff;
13039       }
13040       else if (vp_playfield->align == VALIGN_MIDDLE)
13041       {
13042         int border_top_diff    = playfield_ydiff / 2;
13043         int border_bottom_diff = playfield_ydiff - border_top_diff;
13044
13045         vp_playfield->border_top    += border_top_diff;
13046         vp_playfield->border_bottom += border_bottom_diff;
13047       }
13048     }
13049
13050     // adjust door positions according to specified alignment
13051
13052     for (j = 0; j < 2; j++)
13053     {
13054       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13055
13056       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13057         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13058       else if (vp_door->align == ALIGN_CENTER)
13059         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13060       else if (vp_door->align == ALIGN_RIGHT)
13061         vp_door->x += vp_window->width - vp_door->width;
13062
13063       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13064         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13065       else if (vp_door->valign == VALIGN_MIDDLE)
13066         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13067       else if (vp_door->valign == VALIGN_BOTTOM)
13068         vp_door->y += vp_window->height - vp_door->height;
13069     }
13070   }
13071 }
13072
13073 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13074 {
13075   static struct
13076   {
13077     struct XYTileSize *dst, *src;
13078     int graphic;
13079   }
13080   editor_buttons_xy[] =
13081   {
13082     {
13083       &editor.button.element_left,      &editor.palette.element_left,
13084       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13085     },
13086     {
13087       &editor.button.element_middle,    &editor.palette.element_middle,
13088       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13089     },
13090     {
13091       &editor.button.element_right,     &editor.palette.element_right,
13092       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13093     },
13094
13095     { NULL,                     NULL                    }
13096   };
13097   int i;
13098
13099   // set default position for element buttons to element graphics
13100   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13101   {
13102     if ((*editor_buttons_xy[i].dst).x == -1 &&
13103         (*editor_buttons_xy[i].dst).y == -1)
13104     {
13105       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13106
13107       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13108
13109       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13110     }
13111   }
13112
13113   // adjust editor palette rows and columns if specified to be dynamic
13114
13115   if (editor.palette.cols == -1)
13116   {
13117     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13118     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13119     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13120
13121     editor.palette.cols = (vp_width - sc_width) / bt_width;
13122
13123     if (editor.palette.x == -1)
13124     {
13125       int palette_width = editor.palette.cols * bt_width + sc_width;
13126
13127       editor.palette.x = (vp_width - palette_width) / 2;
13128     }
13129   }
13130
13131   if (editor.palette.rows == -1)
13132   {
13133     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13134     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13135     int tx_height = getFontHeight(FONT_TEXT_2);
13136
13137     editor.palette.rows = (vp_height - tx_height) / bt_height;
13138
13139     if (editor.palette.y == -1)
13140     {
13141       int palette_height = editor.palette.rows * bt_height + tx_height;
13142
13143       editor.palette.y = (vp_height - palette_height) / 2;
13144     }
13145   }
13146 }
13147
13148 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13149                                                       boolean initialize)
13150 {
13151   // special case: check if network and preview player positions are redefined,
13152   // to compare this later against the main menu level preview being redefined
13153   struct TokenIntPtrInfo menu_config_players[] =
13154   {
13155     { "main.network_players.x", &menu.main.network_players.redefined    },
13156     { "main.network_players.y", &menu.main.network_players.redefined    },
13157     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13158     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13159     { "preview.x",              &preview.redefined                      },
13160     { "preview.y",              &preview.redefined                      }
13161   };
13162   int i;
13163
13164   if (initialize)
13165   {
13166     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13167       *menu_config_players[i].value = FALSE;
13168   }
13169   else
13170   {
13171     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13172       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13173         *menu_config_players[i].value = TRUE;
13174   }
13175 }
13176
13177 static void InitMenuDesignSettings_PreviewPlayers(void)
13178 {
13179   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13180 }
13181
13182 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13183 {
13184   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13185 }
13186
13187 static void LoadMenuDesignSettingsFromFilename(char *filename)
13188 {
13189   static struct TitleFadingInfo tfi;
13190   static struct TitleMessageInfo tmi;
13191   static struct TokenInfo title_tokens[] =
13192   {
13193     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13194     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13195     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13196     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13197     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13198
13199     { -1,               NULL,                   NULL                    }
13200   };
13201   static struct TokenInfo titlemessage_tokens[] =
13202   {
13203     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13204     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13205     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13206     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13207     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13208     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13209     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13210     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13211     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13212     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13213     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13214     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13215     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13216     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13217     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13218     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13219     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13220     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13221
13222     { -1,               NULL,                   NULL                    }
13223   };
13224   static struct
13225   {
13226     struct TitleFadingInfo *info;
13227     char *text;
13228   }
13229   title_info[] =
13230   {
13231     // initialize first titles from "enter screen" definitions, if defined
13232     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13233     { &title_first_default,             "menu.enter_screen.TITLE"       },
13234
13235     // initialize title screens from "next screen" definitions, if defined
13236     { &title_initial_default,           "menu.next_screen.TITLE"        },
13237     { &title_default,                   "menu.next_screen.TITLE"        },
13238
13239     { NULL,                             NULL                            }
13240   };
13241   static struct
13242   {
13243     struct TitleMessageInfo *array;
13244     char *text;
13245   }
13246   titlemessage_arrays[] =
13247   {
13248     // initialize first titles from "enter screen" definitions, if defined
13249     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13250     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13251     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13252     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13253
13254     // initialize titles from "next screen" definitions, if defined
13255     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13256     { titlescreen,                      "menu.next_screen.TITLE"        },
13257     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13258     { titlemessage,                     "menu.next_screen.TITLE"        },
13259
13260     // overwrite titles with title definitions, if defined
13261     { titlescreen_initial_first,        "[title_initial]"               },
13262     { titlescreen_first,                "[title]"                       },
13263     { titlemessage_initial_first,       "[title_initial]"               },
13264     { titlemessage_first,               "[title]"                       },
13265
13266     { titlescreen_initial,              "[title_initial]"               },
13267     { titlescreen,                      "[title]"                       },
13268     { titlemessage_initial,             "[title_initial]"               },
13269     { titlemessage,                     "[title]"                       },
13270
13271     // overwrite titles with title screen/message definitions, if defined
13272     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13273     { titlescreen_first,                "[titlescreen]"                 },
13274     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13275     { titlemessage_first,               "[titlemessage]"                },
13276
13277     { titlescreen_initial,              "[titlescreen_initial]"         },
13278     { titlescreen,                      "[titlescreen]"                 },
13279     { titlemessage_initial,             "[titlemessage_initial]"        },
13280     { titlemessage,                     "[titlemessage]"                },
13281
13282     { NULL,                             NULL                            }
13283   };
13284   SetupFileHash *setup_file_hash;
13285   int i, j, k;
13286
13287   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13288     return;
13289
13290   // the following initializes hierarchical values from dynamic configuration
13291
13292   // special case: initialize with default values that may be overwritten
13293   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13294   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13295   {
13296     struct TokenIntPtrInfo menu_config[] =
13297     {
13298       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13299       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13300       { "menu.list_size",       &menu.list_size[i]      }
13301     };
13302
13303     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13304     {
13305       char *token = menu_config[j].token;
13306       char *value = getHashEntry(setup_file_hash, token);
13307
13308       if (value != NULL)
13309         *menu_config[j].value = get_integer_from_string(value);
13310     }
13311   }
13312
13313   // special case: initialize with default values that may be overwritten
13314   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13315   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13316   {
13317     struct TokenIntPtrInfo menu_config[] =
13318     {
13319       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13320       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13321       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13322       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13323       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13324     };
13325
13326     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13327     {
13328       char *token = menu_config[j].token;
13329       char *value = getHashEntry(setup_file_hash, token);
13330
13331       if (value != NULL)
13332         *menu_config[j].value = get_integer_from_string(value);
13333     }
13334   }
13335
13336   // special case: initialize with default values that may be overwritten
13337   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13338   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13339   {
13340     struct TokenIntPtrInfo menu_config[] =
13341     {
13342       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13343       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13344     };
13345
13346     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13347     {
13348       char *token = menu_config[j].token;
13349       char *value = getHashEntry(setup_file_hash, token);
13350
13351       if (value != NULL)
13352         *menu_config[j].value = get_integer_from_string(value);
13353     }
13354   }
13355
13356   // special case: initialize with default values that may be overwritten
13357   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13358   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13359   {
13360     struct TokenIntPtrInfo menu_config[] =
13361     {
13362       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13363       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13364       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13365       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13366       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13367       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13368       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13369       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13370       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13371       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13372     };
13373
13374     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13375     {
13376       char *token = menu_config[j].token;
13377       char *value = getHashEntry(setup_file_hash, token);
13378
13379       if (value != NULL)
13380         *menu_config[j].value = get_integer_from_string(value);
13381     }
13382   }
13383
13384   // special case: initialize with default values that may be overwritten
13385   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13386   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13387   {
13388     struct TokenIntPtrInfo menu_config[] =
13389     {
13390       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13391       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13392       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13393       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13394       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13395       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13396       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13397       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13398       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13399     };
13400
13401     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13402     {
13403       char *token = menu_config[j].token;
13404       char *value = getHashEntry(setup_file_hash, token);
13405
13406       if (value != NULL)
13407         *menu_config[j].value = get_token_parameter_value(token, value);
13408     }
13409   }
13410
13411   // special case: initialize with default values that may be overwritten
13412   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13413   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13414   {
13415     struct
13416     {
13417       char *token_prefix;
13418       struct RectWithBorder *struct_ptr;
13419     }
13420     vp_struct[] =
13421     {
13422       { "viewport.window",      &viewport.window[i]     },
13423       { "viewport.playfield",   &viewport.playfield[i]  },
13424       { "viewport.door_1",      &viewport.door_1[i]     },
13425       { "viewport.door_2",      &viewport.door_2[i]     }
13426     };
13427
13428     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13429     {
13430       struct TokenIntPtrInfo vp_config[] =
13431       {
13432         { ".x",                 &vp_struct[j].struct_ptr->x             },
13433         { ".y",                 &vp_struct[j].struct_ptr->y             },
13434         { ".width",             &vp_struct[j].struct_ptr->width         },
13435         { ".height",            &vp_struct[j].struct_ptr->height        },
13436         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13437         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13438         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13439         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13440         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13441         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13442         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13443         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13444         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13445         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13446         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13447         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13448         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13449         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13450         { ".align",             &vp_struct[j].struct_ptr->align         },
13451         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13452       };
13453
13454       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13455       {
13456         char *token = getStringCat2(vp_struct[j].token_prefix,
13457                                     vp_config[k].token);
13458         char *value = getHashEntry(setup_file_hash, token);
13459
13460         if (value != NULL)
13461           *vp_config[k].value = get_token_parameter_value(token, value);
13462
13463         free(token);
13464       }
13465     }
13466   }
13467
13468   // special case: initialize with default values that may be overwritten
13469   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13470   for (i = 0; title_info[i].info != NULL; i++)
13471   {
13472     struct TitleFadingInfo *info = title_info[i].info;
13473     char *base_token = title_info[i].text;
13474
13475     for (j = 0; title_tokens[j].type != -1; j++)
13476     {
13477       char *token = getStringCat2(base_token, title_tokens[j].text);
13478       char *value = getHashEntry(setup_file_hash, token);
13479
13480       if (value != NULL)
13481       {
13482         int parameter_value = get_token_parameter_value(token, value);
13483
13484         tfi = *info;
13485
13486         *(int *)title_tokens[j].value = (int)parameter_value;
13487
13488         *info = tfi;
13489       }
13490
13491       free(token);
13492     }
13493   }
13494
13495   // special case: initialize with default values that may be overwritten
13496   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13497   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13498   {
13499     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13500     char *base_token = titlemessage_arrays[i].text;
13501
13502     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13503     {
13504       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13505       char *value = getHashEntry(setup_file_hash, token);
13506
13507       if (value != NULL)
13508       {
13509         int parameter_value = get_token_parameter_value(token, value);
13510
13511         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13512         {
13513           tmi = array[k];
13514
13515           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13516             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13517           else
13518             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13519
13520           array[k] = tmi;
13521         }
13522       }
13523
13524       free(token);
13525     }
13526   }
13527
13528   // read (and overwrite with) values that may be specified in config file
13529   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13530
13531   // special case: check if network and preview player positions are redefined
13532   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13533
13534   freeSetupFileHash(setup_file_hash);
13535 }
13536
13537 void LoadMenuDesignSettings(void)
13538 {
13539   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13540
13541   InitMenuDesignSettings_Static();
13542   InitMenuDesignSettings_SpecialPreProcessing();
13543   InitMenuDesignSettings_PreviewPlayers();
13544
13545   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13546   {
13547     // first look for special settings configured in level series config
13548     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13549
13550     if (fileExists(filename_base))
13551       LoadMenuDesignSettingsFromFilename(filename_base);
13552   }
13553
13554   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13555
13556   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13557     LoadMenuDesignSettingsFromFilename(filename_local);
13558
13559   InitMenuDesignSettings_SpecialPostProcessing();
13560 }
13561
13562 void LoadMenuDesignSettings_AfterGraphics(void)
13563 {
13564   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13565 }
13566
13567 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13568                                 boolean ignore_defaults)
13569 {
13570   int i;
13571
13572   for (i = 0; sound_config_vars[i].token != NULL; i++)
13573   {
13574     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13575
13576     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13577     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13578       continue;
13579
13580     if (value != NULL)
13581       *sound_config_vars[i].value =
13582         get_token_parameter_value(sound_config_vars[i].token, value);
13583   }
13584 }
13585
13586 void InitSoundSettings_Static(void)
13587 {
13588   // always start with reliable default values from static default config
13589   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13590 }
13591
13592 static void LoadSoundSettingsFromFilename(char *filename)
13593 {
13594   SetupFileHash *setup_file_hash;
13595
13596   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13597     return;
13598
13599   // read (and overwrite with) values that may be specified in config file
13600   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13601
13602   freeSetupFileHash(setup_file_hash);
13603 }
13604
13605 void LoadSoundSettings(void)
13606 {
13607   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13608
13609   InitSoundSettings_Static();
13610
13611   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13612   {
13613     // first look for special settings configured in level series config
13614     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13615
13616     if (fileExists(filename_base))
13617       LoadSoundSettingsFromFilename(filename_base);
13618   }
13619
13620   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13621
13622   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13623     LoadSoundSettingsFromFilename(filename_local);
13624 }
13625
13626 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13627 {
13628   char *filename = getEditorSetupFilename();
13629   SetupFileList *setup_file_list, *list;
13630   SetupFileHash *element_hash;
13631   int num_unknown_tokens = 0;
13632   int i;
13633
13634   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13635     return;
13636
13637   element_hash = newSetupFileHash();
13638
13639   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13640     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13641
13642   // determined size may be larger than needed (due to unknown elements)
13643   *num_elements = 0;
13644   for (list = setup_file_list; list != NULL; list = list->next)
13645     (*num_elements)++;
13646
13647   // add space for up to 3 more elements for padding that may be needed
13648   *num_elements += 3;
13649
13650   // free memory for old list of elements, if needed
13651   checked_free(*elements);
13652
13653   // allocate memory for new list of elements
13654   *elements = checked_malloc(*num_elements * sizeof(int));
13655
13656   *num_elements = 0;
13657   for (list = setup_file_list; list != NULL; list = list->next)
13658   {
13659     char *value = getHashEntry(element_hash, list->token);
13660
13661     if (value == NULL)          // try to find obsolete token mapping
13662     {
13663       char *mapped_token = get_mapped_token(list->token);
13664
13665       if (mapped_token != NULL)
13666       {
13667         value = getHashEntry(element_hash, mapped_token);
13668
13669         free(mapped_token);
13670       }
13671     }
13672
13673     if (value != NULL)
13674     {
13675       (*elements)[(*num_elements)++] = atoi(value);
13676     }
13677     else
13678     {
13679       if (num_unknown_tokens == 0)
13680       {
13681         Warn("---");
13682         Warn("unknown token(s) found in config file:");
13683         Warn("- config file: '%s'", filename);
13684
13685         num_unknown_tokens++;
13686       }
13687
13688       Warn("- token: '%s'", list->token);
13689     }
13690   }
13691
13692   if (num_unknown_tokens > 0)
13693     Warn("---");
13694
13695   while (*num_elements % 4)     // pad with empty elements, if needed
13696     (*elements)[(*num_elements)++] = EL_EMPTY;
13697
13698   freeSetupFileList(setup_file_list);
13699   freeSetupFileHash(element_hash);
13700
13701 #if 0
13702   for (i = 0; i < *num_elements; i++)
13703     Debug("editor", "element '%s' [%d]\n",
13704           element_info[(*elements)[i]].token_name, (*elements)[i]);
13705 #endif
13706 }
13707
13708 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13709                                                      boolean is_sound)
13710 {
13711   SetupFileHash *setup_file_hash = NULL;
13712   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13713   char *filename_music, *filename_prefix, *filename_info;
13714   struct
13715   {
13716     char *token;
13717     char **value_ptr;
13718   }
13719   token_to_value_ptr[] =
13720   {
13721     { "title_header",   &tmp_music_file_info.title_header       },
13722     { "artist_header",  &tmp_music_file_info.artist_header      },
13723     { "album_header",   &tmp_music_file_info.album_header       },
13724     { "year_header",    &tmp_music_file_info.year_header        },
13725     { "played_header",  &tmp_music_file_info.played_header      },
13726
13727     { "title",          &tmp_music_file_info.title              },
13728     { "artist",         &tmp_music_file_info.artist             },
13729     { "album",          &tmp_music_file_info.album              },
13730     { "year",           &tmp_music_file_info.year               },
13731     { "played",         &tmp_music_file_info.played             },
13732
13733     { NULL,             NULL                                    },
13734   };
13735   int i;
13736
13737   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13738                     getCustomMusicFilename(basename));
13739
13740   if (filename_music == NULL)
13741     return NULL;
13742
13743   // ---------- try to replace file extension ----------
13744
13745   filename_prefix = getStringCopy(filename_music);
13746   if (strrchr(filename_prefix, '.') != NULL)
13747     *strrchr(filename_prefix, '.') = '\0';
13748   filename_info = getStringCat2(filename_prefix, ".txt");
13749
13750   if (fileExists(filename_info))
13751     setup_file_hash = loadSetupFileHash(filename_info);
13752
13753   free(filename_prefix);
13754   free(filename_info);
13755
13756   if (setup_file_hash == NULL)
13757   {
13758     // ---------- try to add file extension ----------
13759
13760     filename_prefix = getStringCopy(filename_music);
13761     filename_info = getStringCat2(filename_prefix, ".txt");
13762
13763     if (fileExists(filename_info))
13764       setup_file_hash = loadSetupFileHash(filename_info);
13765
13766     free(filename_prefix);
13767     free(filename_info);
13768   }
13769
13770   if (setup_file_hash == NULL)
13771     return NULL;
13772
13773   // ---------- music file info found ----------
13774
13775   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13776
13777   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13778   {
13779     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13780
13781     *token_to_value_ptr[i].value_ptr =
13782       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13783   }
13784
13785   tmp_music_file_info.basename = getStringCopy(basename);
13786   tmp_music_file_info.music = music;
13787   tmp_music_file_info.is_sound = is_sound;
13788
13789   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13790   *new_music_file_info = tmp_music_file_info;
13791
13792   return new_music_file_info;
13793 }
13794
13795 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13796 {
13797   return get_music_file_info_ext(basename, music, FALSE);
13798 }
13799
13800 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13801 {
13802   return get_music_file_info_ext(basename, sound, TRUE);
13803 }
13804
13805 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13806                                      char *basename, boolean is_sound)
13807 {
13808   for (; list != NULL; list = list->next)
13809     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13810       return TRUE;
13811
13812   return FALSE;
13813 }
13814
13815 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13816 {
13817   return music_info_listed_ext(list, basename, FALSE);
13818 }
13819
13820 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13821 {
13822   return music_info_listed_ext(list, basename, TRUE);
13823 }
13824
13825 void LoadMusicInfo(void)
13826 {
13827   int num_music_noconf = getMusicListSize_NoConf();
13828   int num_music = getMusicListSize();
13829   int num_sounds = getSoundListSize();
13830   struct FileInfo *music, *sound;
13831   struct MusicFileInfo *next, **new;
13832
13833   int i;
13834
13835   while (music_file_info != NULL)
13836   {
13837     next = music_file_info->next;
13838
13839     checked_free(music_file_info->basename);
13840
13841     checked_free(music_file_info->title_header);
13842     checked_free(music_file_info->artist_header);
13843     checked_free(music_file_info->album_header);
13844     checked_free(music_file_info->year_header);
13845     checked_free(music_file_info->played_header);
13846
13847     checked_free(music_file_info->title);
13848     checked_free(music_file_info->artist);
13849     checked_free(music_file_info->album);
13850     checked_free(music_file_info->year);
13851     checked_free(music_file_info->played);
13852
13853     free(music_file_info);
13854
13855     music_file_info = next;
13856   }
13857
13858   new = &music_file_info;
13859
13860   // get (configured or unconfigured) music file info for all levels
13861   for (i = leveldir_current->first_level;
13862        i <= leveldir_current->last_level; i++)
13863   {
13864     int music_nr;
13865
13866     if (levelset.music[i] != MUS_UNDEFINED)
13867     {
13868       // get music file info for configured level music
13869       music_nr = levelset.music[i];
13870     }
13871     else if (num_music_noconf > 0)
13872     {
13873       // get music file info for unconfigured level music
13874       int level_pos = i - leveldir_current->first_level;
13875
13876       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13877     }
13878     else
13879     {
13880       continue;
13881     }
13882
13883     char *basename = getMusicInfoEntryFilename(music_nr);
13884
13885     if (basename == NULL)
13886       continue;
13887
13888     if (!music_info_listed(music_file_info, basename))
13889     {
13890       *new = get_music_file_info(basename, music_nr);
13891
13892       if (*new != NULL)
13893         new = &(*new)->next;
13894     }
13895   }
13896
13897   // get music file info for all remaining configured music files
13898   for (i = 0; i < num_music; i++)
13899   {
13900     music = getMusicListEntry(i);
13901
13902     if (music->filename == NULL)
13903       continue;
13904
13905     if (strEqual(music->filename, UNDEFINED_FILENAME))
13906       continue;
13907
13908     // a configured file may be not recognized as music
13909     if (!FileIsMusic(music->filename))
13910       continue;
13911
13912     if (!music_info_listed(music_file_info, music->filename))
13913     {
13914       *new = get_music_file_info(music->filename, i);
13915
13916       if (*new != NULL)
13917         new = &(*new)->next;
13918     }
13919   }
13920
13921   // get sound file info for all configured sound files
13922   for (i = 0; i < num_sounds; i++)
13923   {
13924     sound = getSoundListEntry(i);
13925
13926     if (sound->filename == NULL)
13927       continue;
13928
13929     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13930       continue;
13931
13932     // a configured file may be not recognized as sound
13933     if (!FileIsSound(sound->filename))
13934       continue;
13935
13936     if (!sound_info_listed(music_file_info, sound->filename))
13937     {
13938       *new = get_sound_file_info(sound->filename, i);
13939       if (*new != NULL)
13940         new = &(*new)->next;
13941     }
13942   }
13943
13944   // add pointers to previous list nodes
13945
13946   struct MusicFileInfo *node = music_file_info;
13947
13948   while (node != NULL)
13949   {
13950     if (node->next)
13951       node->next->prev = node;
13952
13953     node = node->next;
13954   }
13955 }
13956
13957 static void add_helpanim_entry(int element, int action, int direction,
13958                                int delay, int *num_list_entries)
13959 {
13960   struct HelpAnimInfo *new_list_entry;
13961   (*num_list_entries)++;
13962
13963   helpanim_info =
13964     checked_realloc(helpanim_info,
13965                     *num_list_entries * sizeof(struct HelpAnimInfo));
13966   new_list_entry = &helpanim_info[*num_list_entries - 1];
13967
13968   new_list_entry->element = element;
13969   new_list_entry->action = action;
13970   new_list_entry->direction = direction;
13971   new_list_entry->delay = delay;
13972 }
13973
13974 static void print_unknown_token(char *filename, char *token, int token_nr)
13975 {
13976   if (token_nr == 0)
13977   {
13978     Warn("---");
13979     Warn("unknown token(s) found in config file:");
13980     Warn("- config file: '%s'", filename);
13981   }
13982
13983   Warn("- token: '%s'", token);
13984 }
13985
13986 static void print_unknown_token_end(int token_nr)
13987 {
13988   if (token_nr > 0)
13989     Warn("---");
13990 }
13991
13992 void LoadHelpAnimInfo(void)
13993 {
13994   char *filename = getHelpAnimFilename();
13995   SetupFileList *setup_file_list = NULL, *list;
13996   SetupFileHash *element_hash, *action_hash, *direction_hash;
13997   int num_list_entries = 0;
13998   int num_unknown_tokens = 0;
13999   int i;
14000
14001   if (fileExists(filename))
14002     setup_file_list = loadSetupFileList(filename);
14003
14004   if (setup_file_list == NULL)
14005   {
14006     // use reliable default values from static configuration
14007     SetupFileList *insert_ptr;
14008
14009     insert_ptr = setup_file_list =
14010       newSetupFileList(helpanim_config[0].token,
14011                        helpanim_config[0].value);
14012
14013     for (i = 1; helpanim_config[i].token; i++)
14014       insert_ptr = addListEntry(insert_ptr,
14015                                 helpanim_config[i].token,
14016                                 helpanim_config[i].value);
14017   }
14018
14019   element_hash   = newSetupFileHash();
14020   action_hash    = newSetupFileHash();
14021   direction_hash = newSetupFileHash();
14022
14023   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14024     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14025
14026   for (i = 0; i < NUM_ACTIONS; i++)
14027     setHashEntry(action_hash, element_action_info[i].suffix,
14028                  i_to_a(element_action_info[i].value));
14029
14030   // do not store direction index (bit) here, but direction value!
14031   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14032     setHashEntry(direction_hash, element_direction_info[i].suffix,
14033                  i_to_a(1 << element_direction_info[i].value));
14034
14035   for (list = setup_file_list; list != NULL; list = list->next)
14036   {
14037     char *element_token, *action_token, *direction_token;
14038     char *element_value, *action_value, *direction_value;
14039     int delay = atoi(list->value);
14040
14041     if (strEqual(list->token, "end"))
14042     {
14043       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14044
14045       continue;
14046     }
14047
14048     /* first try to break element into element/action/direction parts;
14049        if this does not work, also accept combined "element[.act][.dir]"
14050        elements (like "dynamite.active"), which are unique elements */
14051
14052     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14053     {
14054       element_value = getHashEntry(element_hash, list->token);
14055       if (element_value != NULL)        // element found
14056         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14057                            &num_list_entries);
14058       else
14059       {
14060         // no further suffixes found -- this is not an element
14061         print_unknown_token(filename, list->token, num_unknown_tokens++);
14062       }
14063
14064       continue;
14065     }
14066
14067     // token has format "<prefix>.<something>"
14068
14069     action_token = strchr(list->token, '.');    // suffix may be action ...
14070     direction_token = action_token;             // ... or direction
14071
14072     element_token = getStringCopy(list->token);
14073     *strchr(element_token, '.') = '\0';
14074
14075     element_value = getHashEntry(element_hash, element_token);
14076
14077     if (element_value == NULL)          // this is no element
14078     {
14079       element_value = getHashEntry(element_hash, list->token);
14080       if (element_value != NULL)        // combined element found
14081         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14082                            &num_list_entries);
14083       else
14084         print_unknown_token(filename, list->token, num_unknown_tokens++);
14085
14086       free(element_token);
14087
14088       continue;
14089     }
14090
14091     action_value = getHashEntry(action_hash, action_token);
14092
14093     if (action_value != NULL)           // action found
14094     {
14095       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14096                     &num_list_entries);
14097
14098       free(element_token);
14099
14100       continue;
14101     }
14102
14103     direction_value = getHashEntry(direction_hash, direction_token);
14104
14105     if (direction_value != NULL)        // direction found
14106     {
14107       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14108                          &num_list_entries);
14109
14110       free(element_token);
14111
14112       continue;
14113     }
14114
14115     if (strchr(action_token + 1, '.') == NULL)
14116     {
14117       // no further suffixes found -- this is not an action nor direction
14118
14119       element_value = getHashEntry(element_hash, list->token);
14120       if (element_value != NULL)        // combined element found
14121         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14122                            &num_list_entries);
14123       else
14124         print_unknown_token(filename, list->token, num_unknown_tokens++);
14125
14126       free(element_token);
14127
14128       continue;
14129     }
14130
14131     // token has format "<prefix>.<suffix>.<something>"
14132
14133     direction_token = strchr(action_token + 1, '.');
14134
14135     action_token = getStringCopy(action_token);
14136     *strchr(action_token + 1, '.') = '\0';
14137
14138     action_value = getHashEntry(action_hash, action_token);
14139
14140     if (action_value == NULL)           // this is no action
14141     {
14142       element_value = getHashEntry(element_hash, list->token);
14143       if (element_value != NULL)        // combined element found
14144         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14145                            &num_list_entries);
14146       else
14147         print_unknown_token(filename, list->token, num_unknown_tokens++);
14148
14149       free(element_token);
14150       free(action_token);
14151
14152       continue;
14153     }
14154
14155     direction_value = getHashEntry(direction_hash, direction_token);
14156
14157     if (direction_value != NULL)        // direction found
14158     {
14159       add_helpanim_entry(atoi(element_value), atoi(action_value),
14160                          atoi(direction_value), delay, &num_list_entries);
14161
14162       free(element_token);
14163       free(action_token);
14164
14165       continue;
14166     }
14167
14168     // this is no direction
14169
14170     element_value = getHashEntry(element_hash, list->token);
14171     if (element_value != NULL)          // combined element found
14172       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14173                          &num_list_entries);
14174     else
14175       print_unknown_token(filename, list->token, num_unknown_tokens++);
14176
14177     free(element_token);
14178     free(action_token);
14179   }
14180
14181   print_unknown_token_end(num_unknown_tokens);
14182
14183   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14184   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14185
14186   freeSetupFileList(setup_file_list);
14187   freeSetupFileHash(element_hash);
14188   freeSetupFileHash(action_hash);
14189   freeSetupFileHash(direction_hash);
14190
14191 #if 0
14192   for (i = 0; i < num_list_entries; i++)
14193     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14194           EL_NAME(helpanim_info[i].element),
14195           helpanim_info[i].element,
14196           helpanim_info[i].action,
14197           helpanim_info[i].direction,
14198           helpanim_info[i].delay);
14199 #endif
14200 }
14201
14202 void LoadHelpTextInfo(void)
14203 {
14204   char *filename = getHelpTextFilename();
14205   int i;
14206
14207   if (helptext_info != NULL)
14208   {
14209     freeSetupFileHash(helptext_info);
14210     helptext_info = NULL;
14211   }
14212
14213   if (fileExists(filename))
14214     helptext_info = loadSetupFileHash(filename);
14215
14216   if (helptext_info == NULL)
14217   {
14218     // use reliable default values from static configuration
14219     helptext_info = newSetupFileHash();
14220
14221     for (i = 0; helptext_config[i].token; i++)
14222       setHashEntry(helptext_info,
14223                    helptext_config[i].token,
14224                    helptext_config[i].value);
14225   }
14226
14227 #if 0
14228   BEGIN_HASH_ITERATION(helptext_info, itr)
14229   {
14230     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14231           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14232   }
14233   END_HASH_ITERATION(hash, itr)
14234 #endif
14235 }
14236
14237
14238 // ----------------------------------------------------------------------------
14239 // convert levels
14240 // ----------------------------------------------------------------------------
14241
14242 #define MAX_NUM_CONVERT_LEVELS          1000
14243
14244 void ConvertLevels(void)
14245 {
14246   static LevelDirTree *convert_leveldir = NULL;
14247   static int convert_level_nr = -1;
14248   static int num_levels_handled = 0;
14249   static int num_levels_converted = 0;
14250   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14251   int i;
14252
14253   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14254                                                global.convert_leveldir);
14255
14256   if (convert_leveldir == NULL)
14257     Fail("no such level identifier: '%s'", global.convert_leveldir);
14258
14259   leveldir_current = convert_leveldir;
14260
14261   if (global.convert_level_nr != -1)
14262   {
14263     convert_leveldir->first_level = global.convert_level_nr;
14264     convert_leveldir->last_level  = global.convert_level_nr;
14265   }
14266
14267   convert_level_nr = convert_leveldir->first_level;
14268
14269   PrintLine("=", 79);
14270   Print("Converting levels\n");
14271   PrintLine("-", 79);
14272   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14273   Print("Level series name:       '%s'\n", convert_leveldir->name);
14274   Print("Level series author:     '%s'\n", convert_leveldir->author);
14275   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14276   PrintLine("=", 79);
14277   Print("\n");
14278
14279   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14280     levels_failed[i] = FALSE;
14281
14282   while (convert_level_nr <= convert_leveldir->last_level)
14283   {
14284     char *level_filename;
14285     boolean new_level;
14286
14287     level_nr = convert_level_nr++;
14288
14289     Print("Level %03d: ", level_nr);
14290
14291     LoadLevel(level_nr);
14292     if (level.no_level_file || level.no_valid_file)
14293     {
14294       Print("(no level)\n");
14295       continue;
14296     }
14297
14298     Print("converting level ... ");
14299
14300 #if 0
14301     // special case: conversion of some EMC levels as requested by ACME
14302     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14303 #endif
14304
14305     level_filename = getDefaultLevelFilename(level_nr);
14306     new_level = !fileExists(level_filename);
14307
14308     if (new_level)
14309     {
14310       SaveLevel(level_nr);
14311
14312       num_levels_converted++;
14313
14314       Print("converted.\n");
14315     }
14316     else
14317     {
14318       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14319         levels_failed[level_nr] = TRUE;
14320
14321       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14322     }
14323
14324     num_levels_handled++;
14325   }
14326
14327   Print("\n");
14328   PrintLine("=", 79);
14329   Print("Number of levels handled: %d\n", num_levels_handled);
14330   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14331          (num_levels_handled ?
14332           num_levels_converted * 100 / num_levels_handled : 0));
14333   PrintLine("-", 79);
14334   Print("Summary (for automatic parsing by scripts):\n");
14335   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14336          convert_leveldir->identifier, num_levels_converted,
14337          num_levels_handled,
14338          (num_levels_handled ?
14339           num_levels_converted * 100 / num_levels_handled : 0));
14340
14341   if (num_levels_handled != num_levels_converted)
14342   {
14343     Print(", FAILED:");
14344     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14345       if (levels_failed[i])
14346         Print(" %03d", i);
14347   }
14348
14349   Print("\n");
14350   PrintLine("=", 79);
14351
14352   CloseAllAndExit(0);
14353 }
14354
14355
14356 // ----------------------------------------------------------------------------
14357 // create and save images for use in level sketches (raw BMP format)
14358 // ----------------------------------------------------------------------------
14359
14360 void CreateLevelSketchImages(void)
14361 {
14362   Bitmap *bitmap1;
14363   Bitmap *bitmap2;
14364   int i;
14365
14366   InitElementPropertiesGfxElement();
14367
14368   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14369   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14370
14371   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14372   {
14373     int element = getMappedElement(i);
14374     char basename1[16];
14375     char basename2[16];
14376     char *filename1;
14377     char *filename2;
14378
14379     sprintf(basename1, "%04d.bmp", i);
14380     sprintf(basename2, "%04ds.bmp", i);
14381
14382     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14383     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14384
14385     DrawSizedElement(0, 0, element, TILESIZE);
14386     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14387
14388     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14389       Fail("cannot save level sketch image file '%s'", filename1);
14390
14391     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14392     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14393
14394     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14395       Fail("cannot save level sketch image file '%s'", filename2);
14396
14397     free(filename1);
14398     free(filename2);
14399
14400     // create corresponding SQL statements (for normal and small images)
14401     if (i < 1000)
14402     {
14403       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14404       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14405     }
14406
14407     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14408     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14409
14410     // optional: create content for forum level sketch demonstration post
14411     if (options.debug)
14412       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14413   }
14414
14415   FreeBitmap(bitmap1);
14416   FreeBitmap(bitmap2);
14417
14418   if (options.debug)
14419     fprintf(stderr, "\n");
14420
14421   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14422
14423   CloseAllAndExit(0);
14424 }
14425
14426
14427 // ----------------------------------------------------------------------------
14428 // create and save images for element collecting animations (raw BMP format)
14429 // ----------------------------------------------------------------------------
14430
14431 static boolean createCollectImage(int element)
14432 {
14433   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14434 }
14435
14436 void CreateCollectElementImages(void)
14437 {
14438   int i, j;
14439   int num_steps = 8;
14440   int anim_frames = num_steps - 1;
14441   int tile_size = TILESIZE;
14442   int anim_width  = tile_size * anim_frames;
14443   int anim_height = tile_size;
14444   int num_collect_images = 0;
14445   int pos_collect_images = 0;
14446
14447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14448     if (createCollectImage(i))
14449       num_collect_images++;
14450
14451   Info("Creating %d element collecting animation images ...",
14452        num_collect_images);
14453
14454   int dst_width  = anim_width * 2;
14455   int dst_height = anim_height * num_collect_images / 2;
14456   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14457   char *basename_bmp = "RocksCollect.bmp";
14458   char *basename_png = "RocksCollect.png";
14459   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14460   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14461   int len_filename_bmp = strlen(filename_bmp);
14462   int len_filename_png = strlen(filename_png);
14463   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14464   char cmd_convert[max_command_len];
14465
14466   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14467            filename_bmp,
14468            filename_png);
14469
14470   // force using RGBA surface for destination bitmap
14471   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14472                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14473
14474   dst_bitmap->surface =
14475     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14476
14477   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14478   {
14479     if (!createCollectImage(i))
14480       continue;
14481
14482     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14483     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14484     int graphic = el2img(i);
14485     char *token_name = element_info[i].token_name;
14486     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14487     Bitmap *src_bitmap;
14488     int src_x, src_y;
14489
14490     Info("- creating collecting image for '%s' ...", token_name);
14491
14492     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14493
14494     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14495                tile_size, tile_size, 0, 0);
14496
14497     // force using RGBA surface for temporary bitmap (using transparent black)
14498     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14499                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14500
14501     tmp_bitmap->surface =
14502       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14503
14504     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14505
14506     for (j = 0; j < anim_frames; j++)
14507     {
14508       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14509       int frame_size = frame_size_final * num_steps;
14510       int offset = (tile_size - frame_size_final) / 2;
14511       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14512
14513       while (frame_size > frame_size_final)
14514       {
14515         frame_size /= 2;
14516
14517         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14518
14519         FreeBitmap(frame_bitmap);
14520
14521         frame_bitmap = half_bitmap;
14522       }
14523
14524       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14525                        frame_size_final, frame_size_final,
14526                        dst_x + j * tile_size + offset, dst_y + offset);
14527
14528       FreeBitmap(frame_bitmap);
14529     }
14530
14531     tmp_bitmap->surface_masked = NULL;
14532
14533     FreeBitmap(tmp_bitmap);
14534
14535     pos_collect_images++;
14536   }
14537
14538   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14539     Fail("cannot save element collecting image file '%s'", filename_bmp);
14540
14541   FreeBitmap(dst_bitmap);
14542
14543   Info("Converting image file from BMP to PNG ...");
14544
14545   if (system(cmd_convert) != 0)
14546     Fail("converting image file failed");
14547
14548   unlink(filename_bmp);
14549
14550   Info("Done.");
14551
14552   CloseAllAndExit(0);
14553 }
14554
14555
14556 // ----------------------------------------------------------------------------
14557 // create and save images for custom and group elements (raw BMP format)
14558 // ----------------------------------------------------------------------------
14559
14560 void CreateCustomElementImages(char *directory)
14561 {
14562   char *src_basename = "RocksCE-template.ilbm";
14563   char *dst_basename = "RocksCE.bmp";
14564   char *src_filename = getPath2(directory, src_basename);
14565   char *dst_filename = getPath2(directory, dst_basename);
14566   Bitmap *src_bitmap;
14567   Bitmap *bitmap;
14568   int yoffset_ce = 0;
14569   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14570   int i;
14571
14572   InitVideoDefaults();
14573
14574   ReCreateBitmap(&backbuffer, video.width, video.height);
14575
14576   src_bitmap = LoadImage(src_filename);
14577
14578   bitmap = CreateBitmap(TILEX * 16 * 2,
14579                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14580                         DEFAULT_DEPTH);
14581
14582   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14583   {
14584     int x = i % 16;
14585     int y = i / 16;
14586     int ii = i + 1;
14587     int j;
14588
14589     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14590                TILEX * x, TILEY * y + yoffset_ce);
14591
14592     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14593                TILEX, TILEY,
14594                TILEX * x + TILEX * 16,
14595                TILEY * y + yoffset_ce);
14596
14597     for (j = 2; j >= 0; j--)
14598     {
14599       int c = ii % 10;
14600
14601       BlitBitmap(src_bitmap, bitmap,
14602                  TILEX + c * 7, 0, 6, 10,
14603                  TILEX * x + 6 + j * 7,
14604                  TILEY * y + 11 + yoffset_ce);
14605
14606       BlitBitmap(src_bitmap, bitmap,
14607                  TILEX + c * 8, TILEY, 6, 10,
14608                  TILEX * 16 + TILEX * x + 6 + j * 8,
14609                  TILEY * y + 10 + yoffset_ce);
14610
14611       ii /= 10;
14612     }
14613   }
14614
14615   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14616   {
14617     int x = i % 16;
14618     int y = i / 16;
14619     int ii = i + 1;
14620     int j;
14621
14622     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14623                TILEX * x, TILEY * y + yoffset_ge);
14624
14625     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14626                TILEX, TILEY,
14627                TILEX * x + TILEX * 16,
14628                TILEY * y + yoffset_ge);
14629
14630     for (j = 1; j >= 0; j--)
14631     {
14632       int c = ii % 10;
14633
14634       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14635                  TILEX * x + 6 + j * 10,
14636                  TILEY * y + 11 + yoffset_ge);
14637
14638       BlitBitmap(src_bitmap, bitmap,
14639                  TILEX + c * 8, TILEY + 12, 6, 10,
14640                  TILEX * 16 + TILEX * x + 10 + j * 8,
14641                  TILEY * y + 10 + yoffset_ge);
14642
14643       ii /= 10;
14644     }
14645   }
14646
14647   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14648     Fail("cannot save CE graphics file '%s'", dst_filename);
14649
14650   FreeBitmap(bitmap);
14651
14652   CloseAllAndExit(0);
14653 }