extended support for magic wall settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746
747   {
748     EL_BD_ACID,                         -1,
749     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
750     &li.bd_acid_eats_element,           EL_BD_SAND
751   },
752   {
753     EL_BD_ACID,                         -1,
754     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
755     &li.bd_acid_spread_rate,            3
756   },
757   {
758     EL_BD_ACID,                         -1,
759     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
760     &li.bd_acid_turns_to_element,       EL_EMPTY
761   },
762
763   {
764     EL_BD_BITER,                        -1,
765     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
766     &li.bd_biter_move_delay,            0
767   },
768   {
769     EL_BD_BITER,                        -1,
770     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
771     &li.bd_biter_eats_element,          EL_BD_DIAMOND
772   },
773
774   {
775     EL_BD_BLADDER,                      -1,
776     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
777     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
778   },
779
780   {
781     EL_BD_EXPANDABLE_WALL_ANY,          -1,
782     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
783     &li.bd_change_expanding_wall,       FALSE
784   },
785
786   {
787     EL_BD_REPLICATOR,                   -1,
788     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
789     &li.bd_replicators_active,          TRUE
790   },
791   {
792     EL_BD_REPLICATOR,                   -1,
793     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
794     &li.bd_replicator_create_delay,     4
795   },
796
797   {
798     EL_BD_CONVEYOR_LEFT,                -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
800     &li.bd_conveyor_belts_active,       TRUE
801   },
802   {
803     EL_BD_CONVEYOR_LEFT,                -1,
804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
805     &li.bd_conveyor_belts_changed,      FALSE
806   },
807
808   {
809     EL_BD_WATER,                        -1,
810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
811     &li.bd_water_cannot_flow_down,      FALSE
812   },
813
814   // (the following values are related to various game elements)
815
816   {
817     EL_EMERALD,                         -1,
818     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
819     &li.score[SC_EMERALD],              10
820   },
821
822   {
823     EL_DIAMOND,                         -1,
824     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
825     &li.score[SC_DIAMOND],              10
826   },
827
828   {
829     EL_BUG,                             -1,
830     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
831     &li.score[SC_BUG],                  10
832   },
833
834   {
835     EL_SPACESHIP,                       -1,
836     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
837     &li.score[SC_SPACESHIP],            10
838   },
839
840   {
841     EL_PACMAN,                          -1,
842     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
843     &li.score[SC_PACMAN],               10
844   },
845
846   {
847     EL_NUT,                             -1,
848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
849     &li.score[SC_NUT],                  10
850   },
851
852   {
853     EL_DYNAMITE,                        -1,
854     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
855     &li.score[SC_DYNAMITE],             10
856   },
857
858   {
859     EL_KEY_1,                           -1,
860     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
861     &li.score[SC_KEY],                  10
862   },
863
864   {
865     EL_PEARL,                           -1,
866     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
867     &li.score[SC_PEARL],                10
868   },
869
870   {
871     EL_CRYSTAL,                         -1,
872     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
873     &li.score[SC_CRYSTAL],              10
874   },
875
876   // (amoeba values used by R'n'D game engine only)
877   {
878     EL_BD_AMOEBA,                       -1,
879     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
880     &li.amoeba_content,                 EL_DIAMOND
881   },
882   {
883     EL_BD_AMOEBA,                       -1,
884     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
885     &li.amoeba_speed,                   10
886   },
887   {
888     EL_BD_AMOEBA,                       -1,
889     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
890     &li.grow_into_diggable,             TRUE
891   },
892   // (amoeba values used by BD game engine only)
893   {
894     EL_BD_AMOEBA,                       -1,
895     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
896     &li.bd_amoeba_wait_for_hatching,    FALSE
897   },
898   {
899     EL_BD_AMOEBA,                       -1,
900     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
901     &li.bd_amoeba_start_immediately,    TRUE
902   },
903   {
904     EL_BD_AMOEBA,                       -1,
905     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
906     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
907   },
908   {
909     EL_BD_AMOEBA,                       -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
911     &li.bd_amoeba_threshold_too_big,    200
912   },
913   {
914     EL_BD_AMOEBA,                       -1,
915     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
916     &li.bd_amoeba_slow_growth_time,     200
917   },
918   {
919     EL_BD_AMOEBA,                       -1,
920     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
921     &li.bd_amoeba_slow_growth_rate,     3
922   },
923   {
924     EL_BD_AMOEBA,                       -1,
925     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
926     &li.bd_amoeba_fast_growth_rate,     25
927   },
928   {
929     EL_BD_AMOEBA,                       -1,
930     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
931     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
932   },
933   {
934     EL_BD_AMOEBA,                       -1,
935     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
936     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
937   },
938
939   {
940     EL_BD_AMOEBA_2,                     -1,
941     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
942     &li.bd_amoeba_2_threshold_too_big,  200
943   },
944   {
945     EL_BD_AMOEBA_2,                     -1,
946     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
947     &li.bd_amoeba_2_slow_growth_time,   200
948   },
949   {
950     EL_BD_AMOEBA_2,                     -1,
951     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
952     &li.bd_amoeba_2_slow_growth_rate,   3
953   },
954   {
955     EL_BD_AMOEBA_2,                     -1,
956     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
957     &li.bd_amoeba_2_fast_growth_rate,   25
958   },
959   {
960     EL_BD_AMOEBA_2,                     -1,
961     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
962     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
963   },
964   {
965     EL_BD_AMOEBA_2,                     -1,
966     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
967     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
968   },
969   {
970     EL_BD_AMOEBA_2,                     -1,
971     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
972     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
973   },
974   {
975     EL_BD_AMOEBA_2,                     -1,
976     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
977     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
978   },
979
980   {
981     EL_YAMYAM,                          -1,
982     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
983     &li.yamyam_content,                 EL_ROCK, NULL,
984     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
985   },
986   {
987     EL_YAMYAM,                          -1,
988     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
989     &li.score[SC_YAMYAM],               10
990   },
991
992   {
993     EL_ROBOT,                           -1,
994     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
995     &li.score[SC_ROBOT],                10
996   },
997   {
998     EL_ROBOT,                           -1,
999     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1000     &li.slurp_score,                    10
1001   },
1002
1003   {
1004     EL_ROBOT_WHEEL,                     -1,
1005     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1006     &li.time_wheel,                     10
1007   },
1008
1009   {
1010     EL_MAGIC_WALL,                      -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.time_magic_wall,                10
1013   },
1014
1015   {
1016     EL_GAME_OF_LIFE,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1018     &li.game_of_life[0],                2
1019   },
1020   {
1021     EL_GAME_OF_LIFE,                    -1,
1022     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1023     &li.game_of_life[1],                3
1024   },
1025   {
1026     EL_GAME_OF_LIFE,                    -1,
1027     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1028     &li.game_of_life[2],                3
1029   },
1030   {
1031     EL_GAME_OF_LIFE,                    -1,
1032     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1033     &li.game_of_life[3],                3
1034   },
1035   {
1036     EL_GAME_OF_LIFE,                    -1,
1037     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1038     &li.use_life_bugs,                  FALSE
1039   },
1040
1041   {
1042     EL_BIOMAZE,                         -1,
1043     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1044     &li.biomaze[0],                     2
1045   },
1046   {
1047     EL_BIOMAZE,                         -1,
1048     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1049     &li.biomaze[1],                     3
1050   },
1051   {
1052     EL_BIOMAZE,                         -1,
1053     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1054     &li.biomaze[2],                     3
1055   },
1056   {
1057     EL_BIOMAZE,                         -1,
1058     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1059     &li.biomaze[3],                     3
1060   },
1061
1062   {
1063     EL_TIMEGATE_SWITCH,                 -1,
1064     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1065     &li.time_timegate,                  10
1066   },
1067
1068   {
1069     EL_LIGHT_SWITCH_ACTIVE,             -1,
1070     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1071     &li.time_light,                     10
1072   },
1073
1074   {
1075     EL_SHIELD_NORMAL,                   -1,
1076     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1077     &li.shield_normal_time,             10
1078   },
1079   {
1080     EL_SHIELD_NORMAL,                   -1,
1081     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1082     &li.score[SC_SHIELD],               10
1083   },
1084
1085   {
1086     EL_SHIELD_DEADLY,                   -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1088     &li.shield_deadly_time,             10
1089   },
1090   {
1091     EL_SHIELD_DEADLY,                   -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1093     &li.score[SC_SHIELD],               10
1094   },
1095
1096   {
1097     EL_EXTRA_TIME,                      -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.extra_time,                     10
1100   },
1101   {
1102     EL_EXTRA_TIME,                      -1,
1103     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1104     &li.extra_time_score,               10
1105   },
1106
1107   {
1108     EL_TIME_ORB_FULL,                   -1,
1109     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1110     &li.time_orb_time,                  10
1111   },
1112   {
1113     EL_TIME_ORB_FULL,                   -1,
1114     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1115     &li.use_time_orb_bug,               FALSE
1116   },
1117
1118   {
1119     EL_SPRING,                          -1,
1120     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1121     &li.use_spring_bug,                 FALSE
1122   },
1123
1124   {
1125     EL_EMC_ANDROID,                     -1,
1126     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1127     &li.android_move_time,              10
1128   },
1129   {
1130     EL_EMC_ANDROID,                     -1,
1131     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1132     &li.android_clone_time,             10
1133   },
1134   {
1135     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1136     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1137     &li.android_clone_element[0],       EL_EMPTY, NULL,
1138     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1139   },
1140   {
1141     EL_EMC_ANDROID,                     -1,
1142     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1143     &li.android_clone_element[0],       EL_EMPTY, NULL,
1144     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1145   },
1146
1147   {
1148     EL_EMC_LENSES,                      -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1150     &li.lenses_score,                   10
1151   },
1152   {
1153     EL_EMC_LENSES,                      -1,
1154     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1155     &li.lenses_time,                    10
1156   },
1157
1158   {
1159     EL_EMC_MAGNIFIER,                   -1,
1160     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1161     &li.magnify_score,                  10
1162   },
1163   {
1164     EL_EMC_MAGNIFIER,                   -1,
1165     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1166     &li.magnify_time,                   10
1167   },
1168
1169   {
1170     EL_EMC_MAGIC_BALL,                  -1,
1171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1172     &li.ball_time,                      10
1173   },
1174   {
1175     EL_EMC_MAGIC_BALL,                  -1,
1176     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1177     &li.ball_random,                    FALSE
1178   },
1179   {
1180     EL_EMC_MAGIC_BALL,                  -1,
1181     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1182     &li.ball_active_initial,            FALSE
1183   },
1184   {
1185     EL_EMC_MAGIC_BALL,                  -1,
1186     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1187     &li.ball_content,                   EL_EMPTY, NULL,
1188     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1189   },
1190
1191   {
1192     EL_SOKOBAN_FIELD_EMPTY,             -1,
1193     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1194     &li.sb_fields_needed,               TRUE
1195   },
1196
1197   {
1198     EL_SOKOBAN_OBJECT,                  -1,
1199     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1200     &li.sb_objects_needed,              TRUE
1201   },
1202
1203   {
1204     EL_MM_MCDUFFIN,                     -1,
1205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1206     &li.mm_laser_red,                   FALSE
1207   },
1208   {
1209     EL_MM_MCDUFFIN,                     -1,
1210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1211     &li.mm_laser_green,                 FALSE
1212   },
1213   {
1214     EL_MM_MCDUFFIN,                     -1,
1215     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1216     &li.mm_laser_blue,                  TRUE
1217   },
1218
1219   {
1220     EL_DF_LASER,                        -1,
1221     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1222     &li.df_laser_red,                   TRUE
1223   },
1224   {
1225     EL_DF_LASER,                        -1,
1226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1227     &li.df_laser_green,                 TRUE
1228   },
1229   {
1230     EL_DF_LASER,                        -1,
1231     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1232     &li.df_laser_blue,                  FALSE
1233   },
1234
1235   {
1236     EL_MM_FUSE_ACTIVE,                  -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.mm_time_fuse,                   25
1239   },
1240   {
1241     EL_MM_BOMB,                         -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1243     &li.mm_time_bomb,                   75
1244   },
1245
1246   {
1247     EL_MM_GRAY_BALL,                    -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.mm_time_ball,                   75
1250   },
1251   {
1252     EL_MM_GRAY_BALL,                    -1,
1253     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1254     &li.mm_ball_choice_mode,            ANIM_RANDOM
1255   },
1256   {
1257     EL_MM_GRAY_BALL,                    -1,
1258     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1259     &li.mm_ball_content,                EL_EMPTY, NULL,
1260     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1261   },
1262   {
1263     EL_MM_GRAY_BALL,                    -1,
1264     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1265     &li.rotate_mm_ball_content,         TRUE
1266   },
1267   {
1268     EL_MM_GRAY_BALL,                    -1,
1269     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1270     &li.explode_mm_ball,                FALSE
1271   },
1272
1273   {
1274     EL_MM_STEEL_BLOCK,                  -1,
1275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1276     &li.mm_time_block,                  75
1277   },
1278   {
1279     EL_MM_LIGHTBALL,                    -1,
1280     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1281     &li.score[SC_ELEM_BONUS],           10
1282   },
1283
1284   {
1285     -1,                                 -1,
1286     -1,                                 -1,
1287     NULL,                               -1
1288   }
1289 };
1290
1291 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1292 {
1293   {
1294     -1,                                 -1,
1295     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1296     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1297   },
1298   {
1299     -1,                                 -1,
1300     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1301     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1302   },
1303
1304   {
1305     -1,                                 -1,
1306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1307     &xx_envelope.autowrap,              FALSE
1308   },
1309   {
1310     -1,                                 -1,
1311     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1312     &xx_envelope.centered,              FALSE
1313   },
1314
1315   {
1316     -1,                                 -1,
1317     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1318     &xx_envelope.text,                  -1, NULL,
1319     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1320     &xx_default_string_empty[0]
1321   },
1322
1323   {
1324     -1,                                 -1,
1325     -1,                                 -1,
1326     NULL,                               -1
1327   }
1328 };
1329
1330 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1331 {
1332   {
1333     -1,                                 -1,
1334     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1335     &xx_ei.description[0],              -1,
1336     &yy_ei.description[0],
1337     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1338     &xx_default_description[0]
1339   },
1340
1341   {
1342     -1,                                 -1,
1343     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1344     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1345     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1346   },
1347 #if ENABLE_RESERVED_CODE
1348   // (reserved for later use)
1349   {
1350     -1,                                 -1,
1351     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1352     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1353     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1354   },
1355 #endif
1356
1357   {
1358     -1,                                 -1,
1359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1360     &xx_ei.use_gfx_element,             FALSE,
1361     &yy_ei.use_gfx_element
1362   },
1363   {
1364     -1,                                 -1,
1365     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1366     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1367     &yy_ei.gfx_element_initial
1368   },
1369
1370   {
1371     -1,                                 -1,
1372     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1373     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1374     &yy_ei.access_direction
1375   },
1376
1377   {
1378     -1,                                 -1,
1379     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1380     &xx_ei.collect_score_initial,       10,
1381     &yy_ei.collect_score_initial
1382   },
1383   {
1384     -1,                                 -1,
1385     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1386     &xx_ei.collect_count_initial,       1,
1387     &yy_ei.collect_count_initial
1388   },
1389
1390   {
1391     -1,                                 -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1393     &xx_ei.ce_value_fixed_initial,      0,
1394     &yy_ei.ce_value_fixed_initial
1395   },
1396   {
1397     -1,                                 -1,
1398     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1399     &xx_ei.ce_value_random_initial,     0,
1400     &yy_ei.ce_value_random_initial
1401   },
1402   {
1403     -1,                                 -1,
1404     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1405     &xx_ei.use_last_ce_value,           FALSE,
1406     &yy_ei.use_last_ce_value
1407   },
1408
1409   {
1410     -1,                                 -1,
1411     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1412     &xx_ei.push_delay_fixed,            8,
1413     &yy_ei.push_delay_fixed
1414   },
1415   {
1416     -1,                                 -1,
1417     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1418     &xx_ei.push_delay_random,           8,
1419     &yy_ei.push_delay_random
1420   },
1421   {
1422     -1,                                 -1,
1423     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1424     &xx_ei.drop_delay_fixed,            0,
1425     &yy_ei.drop_delay_fixed
1426   },
1427   {
1428     -1,                                 -1,
1429     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1430     &xx_ei.drop_delay_random,           0,
1431     &yy_ei.drop_delay_random
1432   },
1433   {
1434     -1,                                 -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1436     &xx_ei.move_delay_fixed,            0,
1437     &yy_ei.move_delay_fixed
1438   },
1439   {
1440     -1,                                 -1,
1441     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1442     &xx_ei.move_delay_random,           0,
1443     &yy_ei.move_delay_random
1444   },
1445   {
1446     -1,                                 -1,
1447     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1448     &xx_ei.step_delay_fixed,            0,
1449     &yy_ei.step_delay_fixed
1450   },
1451   {
1452     -1,                                 -1,
1453     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1454     &xx_ei.step_delay_random,           0,
1455     &yy_ei.step_delay_random
1456   },
1457
1458   {
1459     -1,                                 -1,
1460     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1461     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1462     &yy_ei.move_pattern
1463   },
1464   {
1465     -1,                                 -1,
1466     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1467     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1468     &yy_ei.move_direction_initial
1469   },
1470   {
1471     -1,                                 -1,
1472     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1473     &xx_ei.move_stepsize,               TILEX / 8,
1474     &yy_ei.move_stepsize
1475   },
1476
1477   {
1478     -1,                                 -1,
1479     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1480     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1481     &yy_ei.move_enter_element
1482   },
1483   {
1484     -1,                                 -1,
1485     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1486     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1487     &yy_ei.move_leave_element
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1492     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1493     &yy_ei.move_leave_type
1494   },
1495
1496   {
1497     -1,                                 -1,
1498     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1499     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1500     &yy_ei.slippery_type
1501   },
1502
1503   {
1504     -1,                                 -1,
1505     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1506     &xx_ei.explosion_type,              EXPLODES_3X3,
1507     &yy_ei.explosion_type
1508   },
1509   {
1510     -1,                                 -1,
1511     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1512     &xx_ei.explosion_delay,             16,
1513     &yy_ei.explosion_delay
1514   },
1515   {
1516     -1,                                 -1,
1517     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1518     &xx_ei.ignition_delay,              8,
1519     &yy_ei.ignition_delay
1520   },
1521
1522   {
1523     -1,                                 -1,
1524     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1525     &xx_ei.content,                     EL_EMPTY_SPACE,
1526     &yy_ei.content,
1527     &xx_num_contents,                   1, 1
1528   },
1529
1530   // ---------- "num_change_pages" must be the last entry ---------------------
1531
1532   {
1533     -1,                                 SAVE_CONF_ALWAYS,
1534     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1535     &xx_ei.num_change_pages,            1,
1536     &yy_ei.num_change_pages
1537   },
1538
1539   {
1540     -1,                                 -1,
1541     -1,                                 -1,
1542     NULL,                               -1,
1543     NULL
1544   }
1545 };
1546
1547 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1548 {
1549   // ---------- "current_change_page" must be the first entry -----------------
1550
1551   {
1552     -1,                                 SAVE_CONF_ALWAYS,
1553     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1554     &xx_current_change_page,            -1
1555   },
1556
1557   // ---------- (the remaining entries can be in any order) -------------------
1558
1559   {
1560     -1,                                 -1,
1561     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1562     &xx_change.can_change,              FALSE
1563   },
1564
1565   {
1566     -1,                                 -1,
1567     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1568     &xx_event_bits[0],                  0
1569   },
1570   {
1571     -1,                                 -1,
1572     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1573     &xx_event_bits[1],                  0
1574   },
1575
1576   {
1577     -1,                                 -1,
1578     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1579     &xx_change.trigger_player,          CH_PLAYER_ANY
1580   },
1581   {
1582     -1,                                 -1,
1583     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1584     &xx_change.trigger_side,            CH_SIDE_ANY
1585   },
1586   {
1587     -1,                                 -1,
1588     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1589     &xx_change.trigger_page,            CH_PAGE_ANY
1590   },
1591
1592   {
1593     -1,                                 -1,
1594     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1595     &xx_change.target_element,          EL_EMPTY_SPACE
1596   },
1597
1598   {
1599     -1,                                 -1,
1600     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1601     &xx_change.delay_fixed,             0
1602   },
1603   {
1604     -1,                                 -1,
1605     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1606     &xx_change.delay_random,            0
1607   },
1608   {
1609     -1,                                 -1,
1610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1611     &xx_change.delay_frames,            FRAMES_PER_SECOND
1612   },
1613
1614   {
1615     -1,                                 -1,
1616     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1617     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1623     &xx_change.explode,                 FALSE
1624   },
1625   {
1626     -1,                                 -1,
1627     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1628     &xx_change.use_target_content,      FALSE
1629   },
1630   {
1631     -1,                                 -1,
1632     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1633     &xx_change.only_if_complete,        FALSE
1634   },
1635   {
1636     -1,                                 -1,
1637     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1638     &xx_change.use_random_replace,      FALSE
1639   },
1640   {
1641     -1,                                 -1,
1642     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1643     &xx_change.random_percentage,       100
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1648     &xx_change.replace_when,            CP_WHEN_EMPTY
1649   },
1650
1651   {
1652     -1,                                 -1,
1653     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1654     &xx_change.has_action,              FALSE
1655   },
1656   {
1657     -1,                                 -1,
1658     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1659     &xx_change.action_type,             CA_NO_ACTION
1660   },
1661   {
1662     -1,                                 -1,
1663     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1664     &xx_change.action_mode,             CA_MODE_UNDEFINED
1665   },
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1669     &xx_change.action_arg,              CA_ARG_UNDEFINED
1670   },
1671
1672   {
1673     -1,                                 -1,
1674     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1675     &xx_change.action_element,          EL_EMPTY_SPACE
1676   },
1677
1678   {
1679     -1,                                 -1,
1680     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1681     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1682     &xx_num_contents,                   1, 1
1683   },
1684
1685   {
1686     -1,                                 -1,
1687     -1,                                 -1,
1688     NULL,                               -1
1689   }
1690 };
1691
1692 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1693 {
1694   {
1695     -1,                                 -1,
1696     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1697     &xx_ei.description[0],              -1, NULL,
1698     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1699     &xx_default_description[0]
1700   },
1701
1702   {
1703     -1,                                 -1,
1704     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1705     &xx_ei.use_gfx_element,             FALSE
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1710     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1711   },
1712
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1716     &xx_group.choice_mode,              ANIM_RANDOM
1717   },
1718
1719   {
1720     -1,                                 -1,
1721     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1722     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1723     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1724   },
1725
1726   {
1727     -1,                                 -1,
1728     -1,                                 -1,
1729     NULL,                               -1
1730   }
1731 };
1732
1733 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1734 {
1735   {
1736     -1,                                 -1,
1737     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1738     &xx_ei.use_gfx_element,             FALSE
1739   },
1740   {
1741     -1,                                 -1,
1742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1743     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     -1,                                 -1,
1749     NULL,                               -1
1750   }
1751 };
1752
1753 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1754 {
1755   {
1756     EL_PLAYER_1,                        -1,
1757     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1758     &li.block_snap_field,               TRUE
1759   },
1760   {
1761     EL_PLAYER_1,                        -1,
1762     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1763     &li.continuous_snapping,            TRUE
1764   },
1765   {
1766     EL_PLAYER_1,                        -1,
1767     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1768     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1769   },
1770   {
1771     EL_PLAYER_1,                        -1,
1772     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1773     &li.use_start_element[0],           FALSE
1774   },
1775   {
1776     EL_PLAYER_1,                        -1,
1777     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1778     &li.start_element[0],               EL_PLAYER_1
1779   },
1780   {
1781     EL_PLAYER_1,                        -1,
1782     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1783     &li.use_artwork_element[0],         FALSE
1784   },
1785   {
1786     EL_PLAYER_1,                        -1,
1787     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1788     &li.artwork_element[0],             EL_PLAYER_1
1789   },
1790   {
1791     EL_PLAYER_1,                        -1,
1792     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1793     &li.use_explosion_element[0],       FALSE
1794   },
1795   {
1796     EL_PLAYER_1,                        -1,
1797     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1798     &li.explosion_element[0],           EL_PLAYER_1
1799   },
1800
1801   {
1802     -1,                                 -1,
1803     -1,                                 -1,
1804     NULL,                               -1
1805   }
1806 };
1807
1808 static struct
1809 {
1810   int filetype;
1811   char *id;
1812 }
1813 filetype_id_list[] =
1814 {
1815   { LEVEL_FILE_TYPE_RND,        "RND"   },
1816   { LEVEL_FILE_TYPE_BD,         "BD"    },
1817   { LEVEL_FILE_TYPE_EM,         "EM"    },
1818   { LEVEL_FILE_TYPE_SP,         "SP"    },
1819   { LEVEL_FILE_TYPE_DX,         "DX"    },
1820   { LEVEL_FILE_TYPE_SB,         "SB"    },
1821   { LEVEL_FILE_TYPE_DC,         "DC"    },
1822   { LEVEL_FILE_TYPE_MM,         "MM"    },
1823   { LEVEL_FILE_TYPE_MM,         "DF"    },
1824   { -1,                         NULL    },
1825 };
1826
1827
1828 // ============================================================================
1829 // level file functions
1830 // ============================================================================
1831
1832 static boolean check_special_flags(char *flag)
1833 {
1834   if (strEqual(options.special_flags, flag) ||
1835       strEqual(leveldir_current->special_flags, flag))
1836     return TRUE;
1837
1838   return FALSE;
1839 }
1840
1841 static struct DateInfo getCurrentDate(void)
1842 {
1843   time_t epoch_seconds = time(NULL);
1844   struct tm *now = localtime(&epoch_seconds);
1845   struct DateInfo date;
1846
1847   date.year  = now->tm_year + 1900;
1848   date.month = now->tm_mon  + 1;
1849   date.day   = now->tm_mday;
1850
1851   date.src   = DATE_SRC_CLOCK;
1852
1853   return date;
1854 }
1855
1856 static void resetEventFlags(struct ElementChangeInfo *change)
1857 {
1858   int i;
1859
1860   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1861     change->has_event[i] = FALSE;
1862 }
1863
1864 static void resetEventBits(void)
1865 {
1866   int i;
1867
1868   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1869     xx_event_bits[i] = 0;
1870 }
1871
1872 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1873 {
1874   int i;
1875
1876   /* important: only change event flag if corresponding event bit is set
1877      (this is because all xx_event_bits[] values are loaded separately,
1878      and all xx_event_bits[] values are set back to zero before loading
1879      another value xx_event_bits[x] (each value representing 32 flags)) */
1880
1881   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1882     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1883       change->has_event[i] = TRUE;
1884 }
1885
1886 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1887 {
1888   int i;
1889
1890   /* in contrast to the above function setEventFlagsFromEventBits(), it
1891      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1892      depending on the corresponding change->has_event[i] values here, as
1893      all xx_event_bits[] values are reset in resetEventBits() before */
1894
1895   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1896     if (change->has_event[i])
1897       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1898 }
1899
1900 static char *getDefaultElementDescription(struct ElementInfo *ei)
1901 {
1902   static char description[MAX_ELEMENT_NAME_LEN + 1];
1903   char *default_description = (ei->custom_description != NULL ?
1904                                ei->custom_description :
1905                                ei->editor_description);
1906   int i;
1907
1908   // always start with reliable default values
1909   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1910     description[i] = '\0';
1911
1912   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1913   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1914
1915   return &description[0];
1916 }
1917
1918 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1919 {
1920   char *default_description = getDefaultElementDescription(ei);
1921   int i;
1922
1923   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1924     ei->description[i] = default_description[i];
1925 }
1926
1927 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1928 {
1929   int i;
1930
1931   for (i = 0; conf[i].data_type != -1; i++)
1932   {
1933     int default_value = conf[i].default_value;
1934     int data_type = conf[i].data_type;
1935     int conf_type = conf[i].conf_type;
1936     int byte_mask = conf_type & CONF_MASK_BYTES;
1937
1938     if (byte_mask == CONF_MASK_MULTI_BYTES)
1939     {
1940       int default_num_entities = conf[i].default_num_entities;
1941       int max_num_entities = conf[i].max_num_entities;
1942
1943       *(int *)(conf[i].num_entities) = default_num_entities;
1944
1945       if (data_type == TYPE_STRING)
1946       {
1947         char *default_string = conf[i].default_string;
1948         char *string = (char *)(conf[i].value);
1949
1950         strncpy(string, default_string, max_num_entities);
1951       }
1952       else if (data_type == TYPE_ELEMENT_LIST)
1953       {
1954         int *element_array = (int *)(conf[i].value);
1955         int j;
1956
1957         for (j = 0; j < max_num_entities; j++)
1958           element_array[j] = default_value;
1959       }
1960       else if (data_type == TYPE_CONTENT_LIST)
1961       {
1962         struct Content *content = (struct Content *)(conf[i].value);
1963         int c, x, y;
1964
1965         for (c = 0; c < max_num_entities; c++)
1966           for (y = 0; y < 3; y++)
1967             for (x = 0; x < 3; x++)
1968               content[c].e[x][y] = default_value;
1969       }
1970     }
1971     else        // constant size configuration data (1, 2 or 4 bytes)
1972     {
1973       if (data_type == TYPE_BOOLEAN)
1974         *(boolean *)(conf[i].value) = default_value;
1975       else
1976         *(int *)    (conf[i].value) = default_value;
1977     }
1978   }
1979 }
1980
1981 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1982 {
1983   int i;
1984
1985   for (i = 0; conf[i].data_type != -1; i++)
1986   {
1987     int data_type = conf[i].data_type;
1988     int conf_type = conf[i].conf_type;
1989     int byte_mask = conf_type & CONF_MASK_BYTES;
1990
1991     if (byte_mask == CONF_MASK_MULTI_BYTES)
1992     {
1993       int max_num_entities = conf[i].max_num_entities;
1994
1995       if (data_type == TYPE_STRING)
1996       {
1997         char *string      = (char *)(conf[i].value);
1998         char *string_copy = (char *)(conf[i].value_copy);
1999
2000         strncpy(string_copy, string, max_num_entities);
2001       }
2002       else if (data_type == TYPE_ELEMENT_LIST)
2003       {
2004         int *element_array      = (int *)(conf[i].value);
2005         int *element_array_copy = (int *)(conf[i].value_copy);
2006         int j;
2007
2008         for (j = 0; j < max_num_entities; j++)
2009           element_array_copy[j] = element_array[j];
2010       }
2011       else if (data_type == TYPE_CONTENT_LIST)
2012       {
2013         struct Content *content      = (struct Content *)(conf[i].value);
2014         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2015         int c, x, y;
2016
2017         for (c = 0; c < max_num_entities; c++)
2018           for (y = 0; y < 3; y++)
2019             for (x = 0; x < 3; x++)
2020               content_copy[c].e[x][y] = content[c].e[x][y];
2021       }
2022     }
2023     else        // constant size configuration data (1, 2 or 4 bytes)
2024     {
2025       if (data_type == TYPE_BOOLEAN)
2026         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2027       else
2028         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2029     }
2030   }
2031 }
2032
2033 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2034 {
2035   int i;
2036
2037   xx_ei = *ei_from;     // copy element data into temporary buffer
2038   yy_ei = *ei_to;       // copy element data into temporary buffer
2039
2040   copyConfigFromConfigList(chunk_config_CUSX_base);
2041
2042   *ei_from = xx_ei;
2043   *ei_to   = yy_ei;
2044
2045   // ---------- reinitialize and copy change pages ----------
2046
2047   ei_to->num_change_pages = ei_from->num_change_pages;
2048   ei_to->current_change_page = ei_from->current_change_page;
2049
2050   setElementChangePages(ei_to, ei_to->num_change_pages);
2051
2052   for (i = 0; i < ei_to->num_change_pages; i++)
2053     ei_to->change_page[i] = ei_from->change_page[i];
2054
2055   // ---------- copy group element info ----------
2056   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2057     *ei_to->group = *ei_from->group;
2058
2059   // mark this custom element as modified
2060   ei_to->modified_settings = TRUE;
2061 }
2062
2063 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2064 {
2065   int change_page_size = sizeof(struct ElementChangeInfo);
2066
2067   ei->num_change_pages = MAX(1, change_pages);
2068
2069   ei->change_page =
2070     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2071
2072   if (ei->current_change_page >= ei->num_change_pages)
2073     ei->current_change_page = ei->num_change_pages - 1;
2074
2075   ei->change = &ei->change_page[ei->current_change_page];
2076 }
2077
2078 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2079 {
2080   xx_change = *change;          // copy change data into temporary buffer
2081
2082   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2083
2084   *change = xx_change;
2085
2086   resetEventFlags(change);
2087
2088   change->direct_action = 0;
2089   change->other_action = 0;
2090
2091   change->pre_change_function = NULL;
2092   change->change_function = NULL;
2093   change->post_change_function = NULL;
2094 }
2095
2096 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2097 {
2098   int i, x, y;
2099
2100   li = *level;          // copy level data into temporary buffer
2101   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2102   *level = li;          // copy temporary buffer back to level data
2103
2104   setLevelInfoToDefaults_BD();
2105   setLevelInfoToDefaults_EM();
2106   setLevelInfoToDefaults_SP();
2107   setLevelInfoToDefaults_MM();
2108
2109   level->native_bd_level = &native_bd_level;
2110   level->native_em_level = &native_em_level;
2111   level->native_sp_level = &native_sp_level;
2112   level->native_mm_level = &native_mm_level;
2113
2114   level->file_version = FILE_VERSION_ACTUAL;
2115   level->game_version = GAME_VERSION_ACTUAL;
2116
2117   level->creation_date = getCurrentDate();
2118
2119   level->encoding_16bit_field  = TRUE;
2120   level->encoding_16bit_yamyam = TRUE;
2121   level->encoding_16bit_amoeba = TRUE;
2122
2123   // clear level name and level author string buffers
2124   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2125     level->name[i] = '\0';
2126   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2127     level->author[i] = '\0';
2128
2129   // set level name and level author to default values
2130   strcpy(level->name, NAMELESS_LEVEL_NAME);
2131   strcpy(level->author, ANONYMOUS_NAME);
2132
2133   // set level playfield to playable default level with player and exit
2134   for (x = 0; x < MAX_LEV_FIELDX; x++)
2135     for (y = 0; y < MAX_LEV_FIELDY; y++)
2136       level->field[x][y] = EL_SAND;
2137
2138   level->field[0][0] = EL_PLAYER_1;
2139   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2140
2141   BorderElement = EL_STEELWALL;
2142
2143   // detect custom elements when loading them
2144   level->file_has_custom_elements = FALSE;
2145
2146   // set all bug compatibility flags to "false" => do not emulate this bug
2147   level->use_action_after_change_bug = FALSE;
2148
2149   if (leveldir_current)
2150   {
2151     // try to determine better author name than 'anonymous'
2152     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2153     {
2154       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2155       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2156     }
2157     else
2158     {
2159       switch (LEVELCLASS(leveldir_current))
2160       {
2161         case LEVELCLASS_TUTORIAL:
2162           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2163           break;
2164
2165         case LEVELCLASS_CONTRIB:
2166           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2167           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2168           break;
2169
2170         case LEVELCLASS_PRIVATE:
2171           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2172           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2173           break;
2174
2175         default:
2176           // keep default value
2177           break;
2178       }
2179     }
2180   }
2181 }
2182
2183 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2184 {
2185   static boolean clipboard_elements_initialized = FALSE;
2186   int i;
2187
2188   InitElementPropertiesStatic();
2189
2190   li = *level;          // copy level data into temporary buffer
2191   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2192   *level = li;          // copy temporary buffer back to level data
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195   {
2196     int element = i;
2197     struct ElementInfo *ei = &element_info[element];
2198
2199     if (element == EL_MM_GRAY_BALL)
2200     {
2201       struct LevelInfo_MM *level_mm = level->native_mm_level;
2202       int j;
2203
2204       for (j = 0; j < level->num_mm_ball_contents; j++)
2205         level->mm_ball_content[j] =
2206           map_element_MM_to_RND(level_mm->ball_content[j]);
2207     }
2208
2209     // never initialize clipboard elements after the very first time
2210     // (to be able to use clipboard elements between several levels)
2211     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2212       continue;
2213
2214     if (IS_ENVELOPE(element))
2215     {
2216       int envelope_nr = element - EL_ENVELOPE_1;
2217
2218       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2219
2220       level->envelope[envelope_nr] = xx_envelope;
2221     }
2222
2223     if (IS_CUSTOM_ELEMENT(element) ||
2224         IS_GROUP_ELEMENT(element) ||
2225         IS_INTERNAL_ELEMENT(element))
2226     {
2227       xx_ei = *ei;      // copy element data into temporary buffer
2228
2229       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2230
2231       *ei = xx_ei;
2232     }
2233
2234     setElementChangePages(ei, 1);
2235     setElementChangeInfoToDefaults(ei->change);
2236
2237     if (IS_CUSTOM_ELEMENT(element) ||
2238         IS_GROUP_ELEMENT(element))
2239     {
2240       setElementDescriptionToDefault(ei);
2241
2242       ei->modified_settings = FALSE;
2243     }
2244
2245     if (IS_CUSTOM_ELEMENT(element) ||
2246         IS_INTERNAL_ELEMENT(element))
2247     {
2248       // internal values used in level editor
2249
2250       ei->access_type = 0;
2251       ei->access_layer = 0;
2252       ei->access_protected = 0;
2253       ei->walk_to_action = 0;
2254       ei->smash_targets = 0;
2255       ei->deadliness = 0;
2256
2257       ei->can_explode_by_fire = FALSE;
2258       ei->can_explode_smashed = FALSE;
2259       ei->can_explode_impact = FALSE;
2260
2261       ei->current_change_page = 0;
2262     }
2263
2264     if (IS_GROUP_ELEMENT(element) ||
2265         IS_INTERNAL_ELEMENT(element))
2266     {
2267       struct ElementGroupInfo *group;
2268
2269       // initialize memory for list of elements in group
2270       if (ei->group == NULL)
2271         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2272
2273       group = ei->group;
2274
2275       xx_group = *group;        // copy group data into temporary buffer
2276
2277       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2278
2279       *group = xx_group;
2280     }
2281
2282     if (IS_EMPTY_ELEMENT(element) ||
2283         IS_INTERNAL_ELEMENT(element))
2284     {
2285       xx_ei = *ei;              // copy element data into temporary buffer
2286
2287       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2288
2289       *ei = xx_ei;
2290     }
2291   }
2292
2293   clipboard_elements_initialized = TRUE;
2294 }
2295
2296 static void setLevelInfoToDefaults(struct LevelInfo *level,
2297                                    boolean level_info_only,
2298                                    boolean reset_file_status)
2299 {
2300   setLevelInfoToDefaults_Level(level);
2301
2302   if (!level_info_only)
2303     setLevelInfoToDefaults_Elements(level);
2304
2305   if (reset_file_status)
2306   {
2307     level->no_valid_file = FALSE;
2308     level->no_level_file = FALSE;
2309   }
2310
2311   level->changed = FALSE;
2312 }
2313
2314 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2315 {
2316   level_file_info->nr = 0;
2317   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2318   level_file_info->packed = FALSE;
2319
2320   setString(&level_file_info->basename, NULL);
2321   setString(&level_file_info->filename, NULL);
2322 }
2323
2324 int getMappedElement_SB(int, boolean);
2325
2326 static void ActivateLevelTemplate(void)
2327 {
2328   int x, y;
2329
2330   if (check_special_flags("load_xsb_to_ces"))
2331   {
2332     // fill smaller playfields with padding "beyond border wall" elements
2333     if (level.fieldx < level_template.fieldx ||
2334         level.fieldy < level_template.fieldy)
2335     {
2336       short field[level.fieldx][level.fieldy];
2337       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2338       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2339       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2340       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2341
2342       // copy old playfield (which is smaller than the visible area)
2343       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2344         field[x][y] = level.field[x][y];
2345
2346       // fill new, larger playfield with "beyond border wall" elements
2347       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2348         level.field[x][y] = getMappedElement_SB('_', TRUE);
2349
2350       // copy the old playfield to the middle of the new playfield
2351       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2352         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2353
2354       level.fieldx = new_fieldx;
2355       level.fieldy = new_fieldy;
2356     }
2357   }
2358
2359   // Currently there is no special action needed to activate the template
2360   // data, because 'element_info' property settings overwrite the original
2361   // level data, while all other variables do not change.
2362
2363   // Exception: 'from_level_template' elements in the original level playfield
2364   // are overwritten with the corresponding elements at the same position in
2365   // playfield from the level template.
2366
2367   for (x = 0; x < level.fieldx; x++)
2368     for (y = 0; y < level.fieldy; y++)
2369       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2370         level.field[x][y] = level_template.field[x][y];
2371
2372   if (check_special_flags("load_xsb_to_ces"))
2373   {
2374     struct LevelInfo level_backup = level;
2375
2376     // overwrite all individual level settings from template level settings
2377     level = level_template;
2378
2379     // restore level file info
2380     level.file_info = level_backup.file_info;
2381
2382     // restore playfield size
2383     level.fieldx = level_backup.fieldx;
2384     level.fieldy = level_backup.fieldy;
2385
2386     // restore playfield content
2387     for (x = 0; x < level.fieldx; x++)
2388       for (y = 0; y < level.fieldy; y++)
2389         level.field[x][y] = level_backup.field[x][y];
2390
2391     // restore name and author from individual level
2392     strcpy(level.name,   level_backup.name);
2393     strcpy(level.author, level_backup.author);
2394
2395     // restore flag "use_custom_template"
2396     level.use_custom_template = level_backup.use_custom_template;
2397   }
2398 }
2399
2400 static boolean checkForPackageFromBasename_BD(char *basename)
2401 {
2402   // check for native BD level file extensions
2403   if (!strSuffixLower(basename, ".bd") &&
2404       !strSuffixLower(basename, ".bdr") &&
2405       !strSuffixLower(basename, ".brc") &&
2406       !strSuffixLower(basename, ".gds"))
2407     return FALSE;
2408
2409   // check for standard single-level BD files (like "001.bd")
2410   if (strSuffixLower(basename, ".bd") &&
2411       strlen(basename) == 6 &&
2412       basename[0] >= '0' && basename[0] <= '9' &&
2413       basename[1] >= '0' && basename[1] <= '9' &&
2414       basename[2] >= '0' && basename[2] <= '9')
2415     return FALSE;
2416
2417   // this is a level package in native BD file format
2418   return TRUE;
2419 }
2420
2421 static char *getLevelFilenameFromBasename(char *basename)
2422 {
2423   static char *filename = NULL;
2424
2425   checked_free(filename);
2426
2427   filename = getPath2(getCurrentLevelDir(), basename);
2428
2429   return filename;
2430 }
2431
2432 static int getFileTypeFromBasename(char *basename)
2433 {
2434   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2435
2436   static char *filename = NULL;
2437   struct stat file_status;
2438
2439   // ---------- try to determine file type from filename ----------
2440
2441   // check for typical filename of a Supaplex level package file
2442   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2443     return LEVEL_FILE_TYPE_SP;
2444
2445   // check for typical filename of a Diamond Caves II level package file
2446   if (strSuffixLower(basename, ".dc") ||
2447       strSuffixLower(basename, ".dc2"))
2448     return LEVEL_FILE_TYPE_DC;
2449
2450   // check for typical filename of a Sokoban level package file
2451   if (strSuffixLower(basename, ".xsb") &&
2452       strchr(basename, '%') == NULL)
2453     return LEVEL_FILE_TYPE_SB;
2454
2455   // check for typical filename of a Boulder Dash (GDash) level package file
2456   if (checkForPackageFromBasename_BD(basename))
2457     return LEVEL_FILE_TYPE_BD;
2458
2459   // ---------- try to determine file type from filesize ----------
2460
2461   checked_free(filename);
2462   filename = getPath2(getCurrentLevelDir(), basename);
2463
2464   if (stat(filename, &file_status) == 0)
2465   {
2466     // check for typical filesize of a Supaplex level package file
2467     if (file_status.st_size == 170496)
2468       return LEVEL_FILE_TYPE_SP;
2469   }
2470
2471   return LEVEL_FILE_TYPE_UNKNOWN;
2472 }
2473
2474 static int getFileTypeFromMagicBytes(char *filename, int type)
2475 {
2476   File *file;
2477
2478   if ((file = openFile(filename, MODE_READ)))
2479   {
2480     char chunk_name[CHUNK_ID_LEN + 1];
2481
2482     getFileChunkBE(file, chunk_name, NULL);
2483
2484     if (strEqual(chunk_name, "MMII") ||
2485         strEqual(chunk_name, "MIRR"))
2486       type = LEVEL_FILE_TYPE_MM;
2487
2488     closeFile(file);
2489   }
2490
2491   return type;
2492 }
2493
2494 static boolean checkForPackageFromBasename(char *basename)
2495 {
2496   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2497   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2498
2499   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2500 }
2501
2502 static char *getSingleLevelBasenameExt(int nr, char *extension)
2503 {
2504   static char basename[MAX_FILENAME_LEN];
2505
2506   if (nr < 0)
2507     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2508   else
2509     sprintf(basename, "%03d.%s", nr, extension);
2510
2511   return basename;
2512 }
2513
2514 static char *getSingleLevelBasename(int nr)
2515 {
2516   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2517 }
2518
2519 static char *getPackedLevelBasename(int type)
2520 {
2521   static char basename[MAX_FILENAME_LEN];
2522   char *directory = getCurrentLevelDir();
2523   Directory *dir;
2524   DirectoryEntry *dir_entry;
2525
2526   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2527
2528   if ((dir = openDirectory(directory)) == NULL)
2529   {
2530     Warn("cannot read current level directory '%s'", directory);
2531
2532     return basename;
2533   }
2534
2535   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2536   {
2537     char *entry_basename = dir_entry->basename;
2538     int entry_type = getFileTypeFromBasename(entry_basename);
2539
2540     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2541     {
2542       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2543           type == entry_type)
2544       {
2545         strcpy(basename, entry_basename);
2546
2547         break;
2548       }
2549     }
2550   }
2551
2552   closeDirectory(dir);
2553
2554   return basename;
2555 }
2556
2557 static char *getSingleLevelFilename(int nr)
2558 {
2559   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2560 }
2561
2562 #if ENABLE_UNUSED_CODE
2563 static char *getPackedLevelFilename(int type)
2564 {
2565   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2566 }
2567 #endif
2568
2569 char *getDefaultLevelFilename(int nr)
2570 {
2571   return getSingleLevelFilename(nr);
2572 }
2573
2574 #if ENABLE_UNUSED_CODE
2575 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2576                                                  int type)
2577 {
2578   lfi->type = type;
2579   lfi->packed = FALSE;
2580
2581   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2582   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2583 }
2584 #endif
2585
2586 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2587                                                  int type, char *format, ...)
2588 {
2589   static char basename[MAX_FILENAME_LEN];
2590   va_list ap;
2591
2592   va_start(ap, format);
2593   vsprintf(basename, format, ap);
2594   va_end(ap);
2595
2596   lfi->type = type;
2597   lfi->packed = FALSE;
2598
2599   setString(&lfi->basename, basename);
2600   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2601 }
2602
2603 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2604                                                  int type)
2605 {
2606   lfi->type = type;
2607   lfi->packed = TRUE;
2608
2609   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2610   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2611 }
2612
2613 static int getFiletypeFromID(char *filetype_id)
2614 {
2615   char *filetype_id_lower;
2616   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2617   int i;
2618
2619   if (filetype_id == NULL)
2620     return LEVEL_FILE_TYPE_UNKNOWN;
2621
2622   filetype_id_lower = getStringToLower(filetype_id);
2623
2624   for (i = 0; filetype_id_list[i].id != NULL; i++)
2625   {
2626     char *id_lower = getStringToLower(filetype_id_list[i].id);
2627     
2628     if (strEqual(filetype_id_lower, id_lower))
2629       filetype = filetype_id_list[i].filetype;
2630
2631     free(id_lower);
2632
2633     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2634       break;
2635   }
2636
2637   free(filetype_id_lower);
2638
2639   return filetype;
2640 }
2641
2642 char *getLocalLevelTemplateFilename(void)
2643 {
2644   return getDefaultLevelFilename(-1);
2645 }
2646
2647 char *getGlobalLevelTemplateFilename(void)
2648 {
2649   // global variable "leveldir_current" must be modified in the loop below
2650   LevelDirTree *leveldir_current_last = leveldir_current;
2651   char *filename = NULL;
2652
2653   // check for template level in path from current to topmost tree node
2654
2655   while (leveldir_current != NULL)
2656   {
2657     filename = getDefaultLevelFilename(-1);
2658
2659     if (fileExists(filename))
2660       break;
2661
2662     leveldir_current = leveldir_current->node_parent;
2663   }
2664
2665   // restore global variable "leveldir_current" modified in above loop
2666   leveldir_current = leveldir_current_last;
2667
2668   return filename;
2669 }
2670
2671 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2672 {
2673   int nr = lfi->nr;
2674
2675   // special case: level number is negative => check for level template file
2676   if (nr < 0)
2677   {
2678     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2679                                          getSingleLevelBasename(-1));
2680
2681     // replace local level template filename with global template filename
2682     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2683
2684     // no fallback if template file not existing
2685     return;
2686   }
2687
2688   // special case: check for file name/pattern specified in "levelinfo.conf"
2689   if (leveldir_current->level_filename != NULL)
2690   {
2691     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2692
2693     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2694                                          leveldir_current->level_filename, nr);
2695
2696     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2697
2698     if (fileExists(lfi->filename))
2699       return;
2700   }
2701   else if (leveldir_current->level_filetype != NULL)
2702   {
2703     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2704
2705     // check for specified native level file with standard file name
2706     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2707                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2708     if (fileExists(lfi->filename))
2709       return;
2710   }
2711
2712   // check for native Rocks'n'Diamonds level file
2713   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2714                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2715   if (fileExists(lfi->filename))
2716     return;
2717
2718   // check for native Boulder Dash level file
2719   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2720   if (fileExists(lfi->filename))
2721     return;
2722
2723   // check for Emerald Mine level file (V1)
2724   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2725                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2726   if (fileExists(lfi->filename))
2727     return;
2728   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2729                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2730   if (fileExists(lfi->filename))
2731     return;
2732
2733   // check for Emerald Mine level file (V2 to V5)
2734   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2735   if (fileExists(lfi->filename))
2736     return;
2737
2738   // check for Emerald Mine level file (V6 / single mode)
2739   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2740   if (fileExists(lfi->filename))
2741     return;
2742   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2743   if (fileExists(lfi->filename))
2744     return;
2745
2746   // check for Emerald Mine level file (V6 / teamwork mode)
2747   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2748   if (fileExists(lfi->filename))
2749     return;
2750   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2751   if (fileExists(lfi->filename))
2752     return;
2753
2754   // check for various packed level file formats
2755   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2756   if (fileExists(lfi->filename))
2757     return;
2758
2759   // no known level file found -- use default values (and fail later)
2760   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2761                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2762 }
2763
2764 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2765 {
2766   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2767     lfi->type = getFileTypeFromBasename(lfi->basename);
2768
2769   if (lfi->type == LEVEL_FILE_TYPE_RND)
2770     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2771 }
2772
2773 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2774 {
2775   // always start with reliable default values
2776   setFileInfoToDefaults(level_file_info);
2777
2778   level_file_info->nr = nr;     // set requested level number
2779
2780   determineLevelFileInfo_Filename(level_file_info);
2781   determineLevelFileInfo_Filetype(level_file_info);
2782 }
2783
2784 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2785                               struct LevelFileInfo *lfi_to)
2786 {
2787   lfi_to->nr     = lfi_from->nr;
2788   lfi_to->type   = lfi_from->type;
2789   lfi_to->packed = lfi_from->packed;
2790
2791   setString(&lfi_to->basename, lfi_from->basename);
2792   setString(&lfi_to->filename, lfi_from->filename);
2793 }
2794
2795 // ----------------------------------------------------------------------------
2796 // functions for loading R'n'D level
2797 // ----------------------------------------------------------------------------
2798
2799 int getMappedElement(int element)
2800 {
2801   // remap some (historic, now obsolete) elements
2802
2803   switch (element)
2804   {
2805     case EL_PLAYER_OBSOLETE:
2806       element = EL_PLAYER_1;
2807       break;
2808
2809     case EL_KEY_OBSOLETE:
2810       element = EL_KEY_1;
2811       break;
2812
2813     case EL_EM_KEY_1_FILE_OBSOLETE:
2814       element = EL_EM_KEY_1;
2815       break;
2816
2817     case EL_EM_KEY_2_FILE_OBSOLETE:
2818       element = EL_EM_KEY_2;
2819       break;
2820
2821     case EL_EM_KEY_3_FILE_OBSOLETE:
2822       element = EL_EM_KEY_3;
2823       break;
2824
2825     case EL_EM_KEY_4_FILE_OBSOLETE:
2826       element = EL_EM_KEY_4;
2827       break;
2828
2829     case EL_ENVELOPE_OBSOLETE:
2830       element = EL_ENVELOPE_1;
2831       break;
2832
2833     case EL_SP_EMPTY:
2834       element = EL_EMPTY;
2835       break;
2836
2837     default:
2838       if (element >= NUM_FILE_ELEMENTS)
2839       {
2840         Warn("invalid level element %d", element);
2841
2842         element = EL_UNKNOWN;
2843       }
2844       break;
2845   }
2846
2847   return element;
2848 }
2849
2850 static int getMappedElementByVersion(int element, int game_version)
2851 {
2852   // remap some elements due to certain game version
2853
2854   if (game_version <= VERSION_IDENT(2,2,0,0))
2855   {
2856     // map game font elements
2857     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2858                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2859                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2860                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2861   }
2862
2863   if (game_version < VERSION_IDENT(3,0,0,0))
2864   {
2865     // map Supaplex gravity tube elements
2866     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2867                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2868                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2869                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2870                element);
2871   }
2872
2873   return element;
2874 }
2875
2876 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2877 {
2878   level->file_version = getFileVersion(file);
2879   level->game_version = getFileVersion(file);
2880
2881   return chunk_size;
2882 }
2883
2884 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2885 {
2886   level->creation_date.year  = getFile16BitBE(file);
2887   level->creation_date.month = getFile8Bit(file);
2888   level->creation_date.day   = getFile8Bit(file);
2889
2890   level->creation_date.src   = DATE_SRC_LEVELFILE;
2891
2892   return chunk_size;
2893 }
2894
2895 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2896 {
2897   int initial_player_stepsize;
2898   int initial_player_gravity;
2899   int i, x, y;
2900
2901   level->fieldx = getFile8Bit(file);
2902   level->fieldy = getFile8Bit(file);
2903
2904   level->time           = getFile16BitBE(file);
2905   level->gems_needed    = getFile16BitBE(file);
2906
2907   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2908     level->name[i] = getFile8Bit(file);
2909   level->name[MAX_LEVEL_NAME_LEN] = 0;
2910
2911   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2912     level->score[i] = getFile8Bit(file);
2913
2914   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2915   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2916     for (y = 0; y < 3; y++)
2917       for (x = 0; x < 3; x++)
2918         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2919
2920   level->amoeba_speed           = getFile8Bit(file);
2921   level->time_magic_wall        = getFile8Bit(file);
2922   level->time_wheel             = getFile8Bit(file);
2923   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2924
2925   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2926                                    STEPSIZE_NORMAL);
2927
2928   for (i = 0; i < MAX_PLAYERS; i++)
2929     level->initial_player_stepsize[i] = initial_player_stepsize;
2930
2931   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2932
2933   for (i = 0; i < MAX_PLAYERS; i++)
2934     level->initial_player_gravity[i] = initial_player_gravity;
2935
2936   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2937   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2938
2939   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2940
2941   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2942   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2943   level->can_move_into_acid_bits = getFile32BitBE(file);
2944   level->dont_collide_with_bits = getFile8Bit(file);
2945
2946   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2947   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2948
2949   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2950   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2951   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2952
2953   level->game_engine_type       = getFile8Bit(file);
2954
2955   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2956
2957   return chunk_size;
2958 }
2959
2960 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2961 {
2962   int i;
2963
2964   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2965     level->name[i] = getFile8Bit(file);
2966   level->name[MAX_LEVEL_NAME_LEN] = 0;
2967
2968   return chunk_size;
2969 }
2970
2971 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2972 {
2973   int i;
2974
2975   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2976     level->author[i] = getFile8Bit(file);
2977   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2978
2979   return chunk_size;
2980 }
2981
2982 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2983 {
2984   int x, y;
2985   int chunk_size_expected = level->fieldx * level->fieldy;
2986
2987   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2988      stored with 16-bit encoding (and should be twice as big then).
2989      Even worse, playfield data was stored 16-bit when only yamyam content
2990      contained 16-bit elements and vice versa. */
2991
2992   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2993     chunk_size_expected *= 2;
2994
2995   if (chunk_size_expected != chunk_size)
2996   {
2997     ReadUnusedBytesFromFile(file, chunk_size);
2998     return chunk_size_expected;
2999   }
3000
3001   for (y = 0; y < level->fieldy; y++)
3002     for (x = 0; x < level->fieldx; x++)
3003       level->field[x][y] =
3004         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3005                          getFile8Bit(file));
3006   return chunk_size;
3007 }
3008
3009 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3010 {
3011   int i, x, y;
3012   int header_size = 4;
3013   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3014   int chunk_size_expected = header_size + content_size;
3015
3016   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3017      stored with 16-bit encoding (and should be twice as big then).
3018      Even worse, playfield data was stored 16-bit when only yamyam content
3019      contained 16-bit elements and vice versa. */
3020
3021   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3022     chunk_size_expected += content_size;
3023
3024   if (chunk_size_expected != chunk_size)
3025   {
3026     ReadUnusedBytesFromFile(file, chunk_size);
3027     return chunk_size_expected;
3028   }
3029
3030   getFile8Bit(file);
3031   level->num_yamyam_contents = getFile8Bit(file);
3032   getFile8Bit(file);
3033   getFile8Bit(file);
3034
3035   // correct invalid number of content fields -- should never happen
3036   if (level->num_yamyam_contents < 1 ||
3037       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3038     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3039
3040   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3041     for (y = 0; y < 3; y++)
3042       for (x = 0; x < 3; x++)
3043         level->yamyam_content[i].e[x][y] =
3044           getMappedElement(level->encoding_16bit_field ?
3045                            getFile16BitBE(file) : getFile8Bit(file));
3046   return chunk_size;
3047 }
3048
3049 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3050 {
3051   int i, x, y;
3052   int element;
3053   int num_contents;
3054   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3055
3056   element = getMappedElement(getFile16BitBE(file));
3057   num_contents = getFile8Bit(file);
3058
3059   getFile8Bit(file);    // content x size (unused)
3060   getFile8Bit(file);    // content y size (unused)
3061
3062   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3063
3064   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3065     for (y = 0; y < 3; y++)
3066       for (x = 0; x < 3; x++)
3067         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3068
3069   // correct invalid number of content fields -- should never happen
3070   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3071     num_contents = STD_ELEMENT_CONTENTS;
3072
3073   if (element == EL_YAMYAM)
3074   {
3075     level->num_yamyam_contents = num_contents;
3076
3077     for (i = 0; i < num_contents; i++)
3078       for (y = 0; y < 3; y++)
3079         for (x = 0; x < 3; x++)
3080           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3081   }
3082   else if (element == EL_BD_AMOEBA)
3083   {
3084     level->amoeba_content = content_array[0][0][0];
3085   }
3086   else
3087   {
3088     Warn("cannot load content for element '%d'", element);
3089   }
3090
3091   return chunk_size;
3092 }
3093
3094 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3095 {
3096   int i;
3097   int element;
3098   int envelope_nr;
3099   int envelope_len;
3100   int chunk_size_expected;
3101
3102   element = getMappedElement(getFile16BitBE(file));
3103   if (!IS_ENVELOPE(element))
3104     element = EL_ENVELOPE_1;
3105
3106   envelope_nr = element - EL_ENVELOPE_1;
3107
3108   envelope_len = getFile16BitBE(file);
3109
3110   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3111   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3112
3113   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3114
3115   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3116   if (chunk_size_expected != chunk_size)
3117   {
3118     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3119     return chunk_size_expected;
3120   }
3121
3122   for (i = 0; i < envelope_len; i++)
3123     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3124
3125   return chunk_size;
3126 }
3127
3128 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3129 {
3130   int num_changed_custom_elements = getFile16BitBE(file);
3131   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3132   int i;
3133
3134   if (chunk_size_expected != chunk_size)
3135   {
3136     ReadUnusedBytesFromFile(file, chunk_size - 2);
3137     return chunk_size_expected;
3138   }
3139
3140   for (i = 0; i < num_changed_custom_elements; i++)
3141   {
3142     int element = getMappedElement(getFile16BitBE(file));
3143     int properties = getFile32BitBE(file);
3144
3145     if (IS_CUSTOM_ELEMENT(element))
3146       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3147     else
3148       Warn("invalid custom element number %d", element);
3149
3150     // older game versions that wrote level files with CUS1 chunks used
3151     // different default push delay values (not yet stored in level file)
3152     element_info[element].push_delay_fixed = 2;
3153     element_info[element].push_delay_random = 8;
3154   }
3155
3156   level->file_has_custom_elements = TRUE;
3157
3158   return chunk_size;
3159 }
3160
3161 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3162 {
3163   int num_changed_custom_elements = getFile16BitBE(file);
3164   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3165   int i;
3166
3167   if (chunk_size_expected != chunk_size)
3168   {
3169     ReadUnusedBytesFromFile(file, chunk_size - 2);
3170     return chunk_size_expected;
3171   }
3172
3173   for (i = 0; i < num_changed_custom_elements; i++)
3174   {
3175     int element = getMappedElement(getFile16BitBE(file));
3176     int custom_target_element = getMappedElement(getFile16BitBE(file));
3177
3178     if (IS_CUSTOM_ELEMENT(element))
3179       element_info[element].change->target_element = custom_target_element;
3180     else
3181       Warn("invalid custom element number %d", element);
3182   }
3183
3184   level->file_has_custom_elements = TRUE;
3185
3186   return chunk_size;
3187 }
3188
3189 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3190 {
3191   int num_changed_custom_elements = getFile16BitBE(file);
3192   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3193   int i, j, x, y;
3194
3195   if (chunk_size_expected != chunk_size)
3196   {
3197     ReadUnusedBytesFromFile(file, chunk_size - 2);
3198     return chunk_size_expected;
3199   }
3200
3201   for (i = 0; i < num_changed_custom_elements; i++)
3202   {
3203     int element = getMappedElement(getFile16BitBE(file));
3204     struct ElementInfo *ei = &element_info[element];
3205     unsigned int event_bits;
3206
3207     if (!IS_CUSTOM_ELEMENT(element))
3208     {
3209       Warn("invalid custom element number %d", element);
3210
3211       element = EL_INTERNAL_DUMMY;
3212     }
3213
3214     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3215       ei->description[j] = getFile8Bit(file);
3216     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3217
3218     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3219
3220     // some free bytes for future properties and padding
3221     ReadUnusedBytesFromFile(file, 7);
3222
3223     ei->use_gfx_element = getFile8Bit(file);
3224     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3225
3226     ei->collect_score_initial = getFile8Bit(file);
3227     ei->collect_count_initial = getFile8Bit(file);
3228
3229     ei->push_delay_fixed = getFile16BitBE(file);
3230     ei->push_delay_random = getFile16BitBE(file);
3231     ei->move_delay_fixed = getFile16BitBE(file);
3232     ei->move_delay_random = getFile16BitBE(file);
3233
3234     ei->move_pattern = getFile16BitBE(file);
3235     ei->move_direction_initial = getFile8Bit(file);
3236     ei->move_stepsize = getFile8Bit(file);
3237
3238     for (y = 0; y < 3; y++)
3239       for (x = 0; x < 3; x++)
3240         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3241
3242     // bits 0 - 31 of "has_event[]"
3243     event_bits = getFile32BitBE(file);
3244     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3245       if (event_bits & (1u << j))
3246         ei->change->has_event[j] = TRUE;
3247
3248     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3249
3250     ei->change->delay_fixed = getFile16BitBE(file);
3251     ei->change->delay_random = getFile16BitBE(file);
3252     ei->change->delay_frames = getFile16BitBE(file);
3253
3254     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3255
3256     ei->change->explode = getFile8Bit(file);
3257     ei->change->use_target_content = getFile8Bit(file);
3258     ei->change->only_if_complete = getFile8Bit(file);
3259     ei->change->use_random_replace = getFile8Bit(file);
3260
3261     ei->change->random_percentage = getFile8Bit(file);
3262     ei->change->replace_when = getFile8Bit(file);
3263
3264     for (y = 0; y < 3; y++)
3265       for (x = 0; x < 3; x++)
3266         ei->change->target_content.e[x][y] =
3267           getMappedElement(getFile16BitBE(file));
3268
3269     ei->slippery_type = getFile8Bit(file);
3270
3271     // some free bytes for future properties and padding
3272     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3273
3274     // mark that this custom element has been modified
3275     ei->modified_settings = TRUE;
3276   }
3277
3278   level->file_has_custom_elements = TRUE;
3279
3280   return chunk_size;
3281 }
3282
3283 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3284 {
3285   struct ElementInfo *ei;
3286   int chunk_size_expected;
3287   int element;
3288   int i, j, x, y;
3289
3290   // ---------- custom element base property values (96 bytes) ----------------
3291
3292   element = getMappedElement(getFile16BitBE(file));
3293
3294   if (!IS_CUSTOM_ELEMENT(element))
3295   {
3296     Warn("invalid custom element number %d", element);
3297
3298     ReadUnusedBytesFromFile(file, chunk_size - 2);
3299
3300     return chunk_size;
3301   }
3302
3303   ei = &element_info[element];
3304
3305   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3306     ei->description[i] = getFile8Bit(file);
3307   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3308
3309   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3310
3311   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3312
3313   ei->num_change_pages = getFile8Bit(file);
3314
3315   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3316   if (chunk_size_expected != chunk_size)
3317   {
3318     ReadUnusedBytesFromFile(file, chunk_size - 43);
3319     return chunk_size_expected;
3320   }
3321
3322   ei->ce_value_fixed_initial = getFile16BitBE(file);
3323   ei->ce_value_random_initial = getFile16BitBE(file);
3324   ei->use_last_ce_value = getFile8Bit(file);
3325
3326   ei->use_gfx_element = getFile8Bit(file);
3327   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3328
3329   ei->collect_score_initial = getFile8Bit(file);
3330   ei->collect_count_initial = getFile8Bit(file);
3331
3332   ei->drop_delay_fixed = getFile8Bit(file);
3333   ei->push_delay_fixed = getFile8Bit(file);
3334   ei->drop_delay_random = getFile8Bit(file);
3335   ei->push_delay_random = getFile8Bit(file);
3336   ei->move_delay_fixed = getFile16BitBE(file);
3337   ei->move_delay_random = getFile16BitBE(file);
3338
3339   // bits 0 - 15 of "move_pattern" ...
3340   ei->move_pattern = getFile16BitBE(file);
3341   ei->move_direction_initial = getFile8Bit(file);
3342   ei->move_stepsize = getFile8Bit(file);
3343
3344   ei->slippery_type = getFile8Bit(file);
3345
3346   for (y = 0; y < 3; y++)
3347     for (x = 0; x < 3; x++)
3348       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3349
3350   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3351   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3352   ei->move_leave_type = getFile8Bit(file);
3353
3354   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3355   ei->move_pattern |= (getFile16BitBE(file) << 16);
3356
3357   ei->access_direction = getFile8Bit(file);
3358
3359   ei->explosion_delay = getFile8Bit(file);
3360   ei->ignition_delay = getFile8Bit(file);
3361   ei->explosion_type = getFile8Bit(file);
3362
3363   // some free bytes for future custom property values and padding
3364   ReadUnusedBytesFromFile(file, 1);
3365
3366   // ---------- change page property values (48 bytes) ------------------------
3367
3368   setElementChangePages(ei, ei->num_change_pages);
3369
3370   for (i = 0; i < ei->num_change_pages; i++)
3371   {
3372     struct ElementChangeInfo *change = &ei->change_page[i];
3373     unsigned int event_bits;
3374
3375     // always start with reliable default values
3376     setElementChangeInfoToDefaults(change);
3377
3378     // bits 0 - 31 of "has_event[]" ...
3379     event_bits = getFile32BitBE(file);
3380     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3381       if (event_bits & (1u << j))
3382         change->has_event[j] = TRUE;
3383
3384     change->target_element = getMappedElement(getFile16BitBE(file));
3385
3386     change->delay_fixed = getFile16BitBE(file);
3387     change->delay_random = getFile16BitBE(file);
3388     change->delay_frames = getFile16BitBE(file);
3389
3390     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3391
3392     change->explode = getFile8Bit(file);
3393     change->use_target_content = getFile8Bit(file);
3394     change->only_if_complete = getFile8Bit(file);
3395     change->use_random_replace = getFile8Bit(file);
3396
3397     change->random_percentage = getFile8Bit(file);
3398     change->replace_when = getFile8Bit(file);
3399
3400     for (y = 0; y < 3; y++)
3401       for (x = 0; x < 3; x++)
3402         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3403
3404     change->can_change = getFile8Bit(file);
3405
3406     change->trigger_side = getFile8Bit(file);
3407
3408     change->trigger_player = getFile8Bit(file);
3409     change->trigger_page = getFile8Bit(file);
3410
3411     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3412                             CH_PAGE_ANY : (1 << change->trigger_page));
3413
3414     change->has_action = getFile8Bit(file);
3415     change->action_type = getFile8Bit(file);
3416     change->action_mode = getFile8Bit(file);
3417     change->action_arg = getFile16BitBE(file);
3418
3419     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3420     event_bits = getFile8Bit(file);
3421     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3422       if (event_bits & (1u << (j - 32)))
3423         change->has_event[j] = TRUE;
3424   }
3425
3426   // mark this custom element as modified
3427   ei->modified_settings = TRUE;
3428
3429   level->file_has_custom_elements = TRUE;
3430
3431   return chunk_size;
3432 }
3433
3434 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3435 {
3436   struct ElementInfo *ei;
3437   struct ElementGroupInfo *group;
3438   int element;
3439   int i;
3440
3441   element = getMappedElement(getFile16BitBE(file));
3442
3443   if (!IS_GROUP_ELEMENT(element))
3444   {
3445     Warn("invalid group element number %d", element);
3446
3447     ReadUnusedBytesFromFile(file, chunk_size - 2);
3448
3449     return chunk_size;
3450   }
3451
3452   ei = &element_info[element];
3453
3454   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3455     ei->description[i] = getFile8Bit(file);
3456   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3457
3458   group = element_info[element].group;
3459
3460   group->num_elements = getFile8Bit(file);
3461
3462   ei->use_gfx_element = getFile8Bit(file);
3463   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3464
3465   group->choice_mode = getFile8Bit(file);
3466
3467   // some free bytes for future values and padding
3468   ReadUnusedBytesFromFile(file, 3);
3469
3470   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3471     group->element[i] = getMappedElement(getFile16BitBE(file));
3472
3473   // mark this group element as modified
3474   element_info[element].modified_settings = TRUE;
3475
3476   level->file_has_custom_elements = TRUE;
3477
3478   return chunk_size;
3479 }
3480
3481 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3482                                 int element, int real_element)
3483 {
3484   int micro_chunk_size = 0;
3485   int conf_type = getFile8Bit(file);
3486   int byte_mask = conf_type & CONF_MASK_BYTES;
3487   boolean element_found = FALSE;
3488   int i;
3489
3490   micro_chunk_size += 1;
3491
3492   if (byte_mask == CONF_MASK_MULTI_BYTES)
3493   {
3494     int num_bytes = getFile16BitBE(file);
3495     byte *buffer = checked_malloc(num_bytes);
3496
3497     ReadBytesFromFile(file, buffer, num_bytes);
3498
3499     for (i = 0; conf[i].data_type != -1; i++)
3500     {
3501       if (conf[i].element == element &&
3502           conf[i].conf_type == conf_type)
3503       {
3504         int data_type = conf[i].data_type;
3505         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3506         int max_num_entities = conf[i].max_num_entities;
3507
3508         if (num_entities > max_num_entities)
3509         {
3510           Warn("truncating number of entities for element %d from %d to %d",
3511                element, num_entities, max_num_entities);
3512
3513           num_entities = max_num_entities;
3514         }
3515
3516         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3517                                   data_type == TYPE_CONTENT_LIST))
3518         {
3519           // for element and content lists, zero entities are not allowed
3520           Warn("found empty list of entities for element %d", element);
3521
3522           // do not set "num_entities" here to prevent reading behind buffer
3523
3524           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3525         }
3526         else
3527         {
3528           *(int *)(conf[i].num_entities) = num_entities;
3529         }
3530
3531         element_found = TRUE;
3532
3533         if (data_type == TYPE_STRING)
3534         {
3535           char *string = (char *)(conf[i].value);
3536           int j;
3537
3538           for (j = 0; j < max_num_entities; j++)
3539             string[j] = (j < num_entities ? buffer[j] : '\0');
3540         }
3541         else if (data_type == TYPE_ELEMENT_LIST)
3542         {
3543           int *element_array = (int *)(conf[i].value);
3544           int j;
3545
3546           for (j = 0; j < num_entities; j++)
3547             element_array[j] =
3548               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3549         }
3550         else if (data_type == TYPE_CONTENT_LIST)
3551         {
3552           struct Content *content= (struct Content *)(conf[i].value);
3553           int c, x, y;
3554
3555           for (c = 0; c < num_entities; c++)
3556             for (y = 0; y < 3; y++)
3557               for (x = 0; x < 3; x++)
3558                 content[c].e[x][y] =
3559                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3560         }
3561         else
3562           element_found = FALSE;
3563
3564         break;
3565       }
3566     }
3567
3568     checked_free(buffer);
3569
3570     micro_chunk_size += 2 + num_bytes;
3571   }
3572   else          // constant size configuration data (1, 2 or 4 bytes)
3573   {
3574     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3575                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3576                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3577
3578     for (i = 0; conf[i].data_type != -1; i++)
3579     {
3580       if (conf[i].element == element &&
3581           conf[i].conf_type == conf_type)
3582       {
3583         int data_type = conf[i].data_type;
3584
3585         if (data_type == TYPE_ELEMENT)
3586           value = getMappedElement(value);
3587
3588         if (data_type == TYPE_BOOLEAN)
3589           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3590         else
3591           *(int *)    (conf[i].value) = value;
3592
3593         element_found = TRUE;
3594
3595         break;
3596       }
3597     }
3598
3599     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3600   }
3601
3602   if (!element_found)
3603   {
3604     char *error_conf_chunk_bytes =
3605       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3606        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3607        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3608     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3609     int error_element = real_element;
3610
3611     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3612          error_conf_chunk_bytes, error_conf_chunk_token,
3613          error_element, EL_NAME(error_element));
3614   }
3615
3616   return micro_chunk_size;
3617 }
3618
3619 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3620 {
3621   int real_chunk_size = 0;
3622
3623   li = *level;          // copy level data into temporary buffer
3624
3625   while (!checkEndOfFile(file))
3626   {
3627     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3628
3629     if (real_chunk_size >= chunk_size)
3630       break;
3631   }
3632
3633   *level = li;          // copy temporary buffer back to level data
3634
3635   return real_chunk_size;
3636 }
3637
3638 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3639 {
3640   int real_chunk_size = 0;
3641
3642   li = *level;          // copy level data into temporary buffer
3643
3644   while (!checkEndOfFile(file))
3645   {
3646     int element = getMappedElement(getFile16BitBE(file));
3647
3648     real_chunk_size += 2;
3649     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3650                                             element, element);
3651     if (real_chunk_size >= chunk_size)
3652       break;
3653   }
3654
3655   *level = li;          // copy temporary buffer back to level data
3656
3657   return real_chunk_size;
3658 }
3659
3660 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3661 {
3662   int real_chunk_size = 0;
3663
3664   li = *level;          // copy level data into temporary buffer
3665
3666   while (!checkEndOfFile(file))
3667   {
3668     int element = getMappedElement(getFile16BitBE(file));
3669
3670     real_chunk_size += 2;
3671     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3672                                             element, element);
3673     if (real_chunk_size >= chunk_size)
3674       break;
3675   }
3676
3677   *level = li;          // copy temporary buffer back to level data
3678
3679   return real_chunk_size;
3680 }
3681
3682 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3683 {
3684   int element = getMappedElement(getFile16BitBE(file));
3685   int envelope_nr = element - EL_ENVELOPE_1;
3686   int real_chunk_size = 2;
3687
3688   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3689
3690   while (!checkEndOfFile(file))
3691   {
3692     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3693                                             -1, element);
3694
3695     if (real_chunk_size >= chunk_size)
3696       break;
3697   }
3698
3699   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3700
3701   return real_chunk_size;
3702 }
3703
3704 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3705 {
3706   int element = getMappedElement(getFile16BitBE(file));
3707   int real_chunk_size = 2;
3708   struct ElementInfo *ei = &element_info[element];
3709   int i;
3710
3711   xx_ei = *ei;          // copy element data into temporary buffer
3712
3713   xx_ei.num_change_pages = -1;
3714
3715   while (!checkEndOfFile(file))
3716   {
3717     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3718                                             -1, element);
3719     if (xx_ei.num_change_pages != -1)
3720       break;
3721
3722     if (real_chunk_size >= chunk_size)
3723       break;
3724   }
3725
3726   *ei = xx_ei;
3727
3728   if (ei->num_change_pages == -1)
3729   {
3730     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3731          EL_NAME(element));
3732
3733     ei->num_change_pages = 1;
3734
3735     setElementChangePages(ei, 1);
3736     setElementChangeInfoToDefaults(ei->change);
3737
3738     return real_chunk_size;
3739   }
3740
3741   // initialize number of change pages stored for this custom element
3742   setElementChangePages(ei, ei->num_change_pages);
3743   for (i = 0; i < ei->num_change_pages; i++)
3744     setElementChangeInfoToDefaults(&ei->change_page[i]);
3745
3746   // start with reading properties for the first change page
3747   xx_current_change_page = 0;
3748
3749   while (!checkEndOfFile(file))
3750   {
3751     // level file might contain invalid change page number
3752     if (xx_current_change_page >= ei->num_change_pages)
3753       break;
3754
3755     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3756
3757     xx_change = *change;        // copy change data into temporary buffer
3758
3759     resetEventBits();           // reset bits; change page might have changed
3760
3761     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3762                                             -1, element);
3763
3764     *change = xx_change;
3765
3766     setEventFlagsFromEventBits(change);
3767
3768     if (real_chunk_size >= chunk_size)
3769       break;
3770   }
3771
3772   level->file_has_custom_elements = TRUE;
3773
3774   return real_chunk_size;
3775 }
3776
3777 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3778 {
3779   int element = getMappedElement(getFile16BitBE(file));
3780   int real_chunk_size = 2;
3781   struct ElementInfo *ei = &element_info[element];
3782   struct ElementGroupInfo *group = ei->group;
3783
3784   if (group == NULL)
3785     return -1;
3786
3787   xx_ei = *ei;          // copy element data into temporary buffer
3788   xx_group = *group;    // copy group data into temporary buffer
3789
3790   while (!checkEndOfFile(file))
3791   {
3792     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3793                                             -1, element);
3794
3795     if (real_chunk_size >= chunk_size)
3796       break;
3797   }
3798
3799   *ei = xx_ei;
3800   *group = xx_group;
3801
3802   level->file_has_custom_elements = TRUE;
3803
3804   return real_chunk_size;
3805 }
3806
3807 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3808 {
3809   int element = getMappedElement(getFile16BitBE(file));
3810   int real_chunk_size = 2;
3811   struct ElementInfo *ei = &element_info[element];
3812
3813   xx_ei = *ei;          // copy element data into temporary buffer
3814
3815   while (!checkEndOfFile(file))
3816   {
3817     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3818                                             -1, element);
3819
3820     if (real_chunk_size >= chunk_size)
3821       break;
3822   }
3823
3824   *ei = xx_ei;
3825
3826   level->file_has_custom_elements = TRUE;
3827
3828   return real_chunk_size;
3829 }
3830
3831 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3832                                       struct LevelFileInfo *level_file_info,
3833                                       boolean level_info_only)
3834 {
3835   char *filename = level_file_info->filename;
3836   char cookie[MAX_LINE_LEN];
3837   char chunk_name[CHUNK_ID_LEN + 1];
3838   int chunk_size;
3839   File *file;
3840
3841   if (!(file = openFile(filename, MODE_READ)))
3842   {
3843     level->no_valid_file = TRUE;
3844     level->no_level_file = TRUE;
3845
3846     if (level_info_only)
3847       return;
3848
3849     Warn("cannot read level '%s' -- using empty level", filename);
3850
3851     if (!setup.editor.use_template_for_new_levels)
3852       return;
3853
3854     // if level file not found, try to initialize level data from template
3855     filename = getGlobalLevelTemplateFilename();
3856
3857     if (!(file = openFile(filename, MODE_READ)))
3858       return;
3859
3860     // default: for empty levels, use level template for custom elements
3861     level->use_custom_template = TRUE;
3862
3863     level->no_valid_file = FALSE;
3864   }
3865
3866   getFileChunkBE(file, chunk_name, NULL);
3867   if (strEqual(chunk_name, "RND1"))
3868   {
3869     getFile32BitBE(file);               // not used
3870
3871     getFileChunkBE(file, chunk_name, NULL);
3872     if (!strEqual(chunk_name, "CAVE"))
3873     {
3874       level->no_valid_file = TRUE;
3875
3876       Warn("unknown format of level file '%s'", filename);
3877
3878       closeFile(file);
3879
3880       return;
3881     }
3882   }
3883   else  // check for pre-2.0 file format with cookie string
3884   {
3885     strcpy(cookie, chunk_name);
3886     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3887       cookie[4] = '\0';
3888     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3889       cookie[strlen(cookie) - 1] = '\0';
3890
3891     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3892     {
3893       level->no_valid_file = TRUE;
3894
3895       Warn("unknown format of level file '%s'", filename);
3896
3897       closeFile(file);
3898
3899       return;
3900     }
3901
3902     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3903     {
3904       level->no_valid_file = TRUE;
3905
3906       Warn("unsupported version of level file '%s'", filename);
3907
3908       closeFile(file);
3909
3910       return;
3911     }
3912
3913     // pre-2.0 level files have no game version, so use file version here
3914     level->game_version = level->file_version;
3915   }
3916
3917   if (level->file_version < FILE_VERSION_1_2)
3918   {
3919     // level files from versions before 1.2.0 without chunk structure
3920     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3921     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3922   }
3923   else
3924   {
3925     static struct
3926     {
3927       char *name;
3928       int size;
3929       int (*loader)(File *, int, struct LevelInfo *);
3930     }
3931     chunk_info[] =
3932     {
3933       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3934       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3935       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3936       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3937       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3938       { "INFO", -1,                     LoadLevel_INFO },
3939       { "BODY", -1,                     LoadLevel_BODY },
3940       { "CONT", -1,                     LoadLevel_CONT },
3941       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3942       { "CNT3", -1,                     LoadLevel_CNT3 },
3943       { "CUS1", -1,                     LoadLevel_CUS1 },
3944       { "CUS2", -1,                     LoadLevel_CUS2 },
3945       { "CUS3", -1,                     LoadLevel_CUS3 },
3946       { "CUS4", -1,                     LoadLevel_CUS4 },
3947       { "GRP1", -1,                     LoadLevel_GRP1 },
3948       { "CONF", -1,                     LoadLevel_CONF },
3949       { "ELEM", -1,                     LoadLevel_ELEM },
3950       { "NOTE", -1,                     LoadLevel_NOTE },
3951       { "CUSX", -1,                     LoadLevel_CUSX },
3952       { "GRPX", -1,                     LoadLevel_GRPX },
3953       { "EMPX", -1,                     LoadLevel_EMPX },
3954
3955       {  NULL,  0,                      NULL }
3956     };
3957
3958     while (getFileChunkBE(file, chunk_name, &chunk_size))
3959     {
3960       int i = 0;
3961
3962       while (chunk_info[i].name != NULL &&
3963              !strEqual(chunk_name, chunk_info[i].name))
3964         i++;
3965
3966       if (chunk_info[i].name == NULL)
3967       {
3968         Warn("unknown chunk '%s' in level file '%s'",
3969              chunk_name, filename);
3970
3971         ReadUnusedBytesFromFile(file, chunk_size);
3972       }
3973       else if (chunk_info[i].size != -1 &&
3974                chunk_info[i].size != chunk_size)
3975       {
3976         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3977              chunk_size, chunk_name, filename);
3978
3979         ReadUnusedBytesFromFile(file, chunk_size);
3980       }
3981       else
3982       {
3983         // call function to load this level chunk
3984         int chunk_size_expected =
3985           (chunk_info[i].loader)(file, chunk_size, level);
3986
3987         if (chunk_size_expected < 0)
3988         {
3989           Warn("error reading chunk '%s' in level file '%s'",
3990                chunk_name, filename);
3991
3992           break;
3993         }
3994
3995         // the size of some chunks cannot be checked before reading other
3996         // chunks first (like "HEAD" and "BODY") that contain some header
3997         // information, so check them here
3998         if (chunk_size_expected != chunk_size)
3999         {
4000           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4001                chunk_size, chunk_name, filename);
4002
4003           break;
4004         }
4005       }
4006     }
4007   }
4008
4009   closeFile(file);
4010 }
4011
4012
4013 // ----------------------------------------------------------------------------
4014 // functions for loading BD level
4015 // ----------------------------------------------------------------------------
4016
4017 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4018 {
4019   struct LevelInfo_BD *level_bd = level->native_bd_level;
4020   GdCave *cave = NULL;  // will be changed below
4021   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4022   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4023   int x, y;
4024
4025   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4026
4027   // cave and map newly allocated when set to defaults above
4028   cave = level_bd->cave;
4029
4030   // level type
4031   cave->intermission                    = level->bd_intermission;
4032
4033   // level settings
4034   cave->level_time[0]                   = level->time;
4035   cave->level_diamonds[0]               = level->gems_needed;
4036
4037   // game timing
4038   cave->scheduling                      = level->bd_scheduling_type;
4039   cave->pal_timing                      = level->bd_pal_timing;
4040   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4041   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4042   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4043   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4044
4045   // scores
4046   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4047   cave->diamond_value                   = level->score[SC_EMERALD];
4048   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4049
4050   // compatibility settings
4051   cave->lineshift                       = level->bd_line_shifting_borders;
4052   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4053   cave->short_explosions                = level->bd_short_explosions;
4054   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4055
4056   // player properties
4057   cave->diagonal_movements              = level->bd_diagonal_movements;
4058   cave->active_is_first_found           = level->bd_topmost_player_active;
4059   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4060   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4061   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4062   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4063
4064   // element properties
4065   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4066   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4067   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4068   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4069   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4070   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4071   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4072   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4073   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4074
4075   cave->magic_diamond_to                = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4076   cave->magic_stone_to                  = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4077   cave->magic_mega_stone_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4078   cave->magic_nut_to                    = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4079   cave->magic_nitro_pack_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4080   cave->magic_flying_diamond_to         = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4081   cave->magic_flying_stone_to           = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4082
4083   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4084   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4085   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4086   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4087   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4088   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4089   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4090   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4091   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4092   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4093   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4094
4095   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4096   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4097   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4098   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4099   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4100   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4101
4102   cave->slime_predictable               = level->bd_slime_is_predictable;
4103   cave->slime_correct_random            = level->bd_slime_correct_random;
4104   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4105   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4106   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4107   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4108
4109   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4110   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4111   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4112
4113   cave->biter_delay_frame               = level->bd_biter_move_delay;
4114   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4115
4116   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4117
4118   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4119
4120   cave->replicators_active              = level->bd_replicators_active;
4121   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4122
4123   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4124   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4125
4126   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4127
4128   // level name
4129   strncpy(cave->name, level->name, sizeof(GdString));
4130   cave->name[sizeof(GdString) - 1] = '\0';
4131
4132   // playfield elements
4133   for (x = 0; x < cave->w; x++)
4134     for (y = 0; y < cave->h; y++)
4135       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4136 }
4137
4138 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4139 {
4140   struct LevelInfo_BD *level_bd = level->native_bd_level;
4141   GdCave *cave = level_bd->cave;
4142   int bd_level_nr = level_bd->level_nr;
4143   int x, y;
4144
4145   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4146   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4147
4148   // level type
4149   level->bd_intermission                = cave->intermission;
4150
4151   // level settings
4152   level->time                           = cave->level_time[bd_level_nr];
4153   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4154
4155   // game timing
4156   level->bd_scheduling_type             = cave->scheduling;
4157   level->bd_pal_timing                  = cave->pal_timing;
4158   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4159   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4160   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4161   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4162
4163   // scores
4164   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4165   level->score[SC_EMERALD]              = cave->diamond_value;
4166   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4167
4168   // compatibility settings
4169   level->bd_line_shifting_borders       = cave->lineshift;
4170   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4171   level->bd_short_explosions            = cave->short_explosions;
4172   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4173
4174   // player properties
4175   level->bd_diagonal_movements          = cave->diagonal_movements;
4176   level->bd_topmost_player_active       = cave->active_is_first_found;
4177   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4178   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4179   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4180   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4181
4182   // element properties
4183   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4184   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4185   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4186   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4187   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4188   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4189   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4190   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4191   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4192
4193   level->bd_magic_wall_diamond_to       = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4194   level->bd_magic_wall_rock_to          = map_element_BD_to_RND_cave(cave->magic_stone_to);
4195   level->bd_magic_wall_mega_rock_to     = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4196   level->bd_magic_wall_nut_to           = map_element_BD_to_RND_cave(cave->magic_nut_to);
4197   level->bd_magic_wall_nitro_pack_to    = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4198   level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4199   level->bd_magic_wall_flying_rock_to   = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4200
4201   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4202   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4203   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4204   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4205   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4206   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4207   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4208   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4209   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4210   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4211   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4212
4213   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4214   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4215   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4216   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4217   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4218   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4219
4220   level->bd_slime_is_predictable        = cave->slime_predictable;
4221   level->bd_slime_correct_random        = cave->slime_correct_random;
4222   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4223   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4224   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4225   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4226
4227   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4228   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4229   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4230
4231   level->bd_biter_move_delay            = cave->biter_delay_frame;
4232   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4233
4234   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4235
4236   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4237
4238   level->bd_replicators_active          = cave->replicators_active;
4239   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4240
4241   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4242   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4243
4244   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4245
4246   // level name
4247   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4248
4249   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4250   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4251
4252   // playfield elements
4253   for (x = 0; x < level->fieldx; x++)
4254     for (y = 0; y < level->fieldy; y++)
4255       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4256
4257   checked_free(cave_name);
4258 }
4259
4260 static void setTapeInfoToDefaults(void);
4261
4262 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4263 {
4264   struct LevelInfo_BD *level_bd = level->native_bd_level;
4265   GdCave *cave = level_bd->cave;
4266   GdReplay *replay = level_bd->replay;
4267   int i;
4268
4269   if (replay == NULL)
4270     return;
4271
4272   // always start with reliable default values
4273   setTapeInfoToDefaults();
4274
4275   tape.level_nr = level_nr;             // (currently not used)
4276   tape.random_seed = replay->seed;
4277
4278   TapeSetDateFromIsoDateString(replay->date);
4279
4280   tape.counter = 0;
4281   tape.pos[tape.counter].delay = 0;
4282
4283   tape.bd_replay = TRUE;
4284
4285   // all time calculations only used to display approximate tape time
4286   int cave_speed = cave->speed;
4287   int milliseconds_game = 0;
4288   int milliseconds_elapsed = 20;
4289
4290   for (i = 0; i < replay->movements->len; i++)
4291   {
4292     int replay_action = replay->movements->data[i];
4293     int tape_action = map_action_BD_to_RND(replay_action);
4294     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4295     boolean success = 0;
4296
4297     while (1)
4298     {
4299       success = TapeAddAction(action);
4300
4301       milliseconds_game += milliseconds_elapsed;
4302
4303       if (milliseconds_game >= cave_speed)
4304       {
4305         milliseconds_game -= cave_speed;
4306
4307         break;
4308       }
4309     }
4310
4311     tape.counter++;
4312     tape.pos[tape.counter].delay = 0;
4313     tape.pos[tape.counter].action[0] = 0;
4314
4315     if (!success)
4316     {
4317       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4318
4319       break;
4320     }
4321   }
4322
4323   TapeHaltRecording();
4324 }
4325
4326
4327 // ----------------------------------------------------------------------------
4328 // functions for loading EM level
4329 // ----------------------------------------------------------------------------
4330
4331 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4332 {
4333   static int ball_xy[8][2] =
4334   {
4335     { 0, 0 },
4336     { 1, 0 },
4337     { 2, 0 },
4338     { 0, 1 },
4339     { 2, 1 },
4340     { 0, 2 },
4341     { 1, 2 },
4342     { 2, 2 },
4343   };
4344   struct LevelInfo_EM *level_em = level->native_em_level;
4345   struct CAVE *cav = level_em->cav;
4346   int i, j, x, y;
4347
4348   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4349   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4350
4351   cav->time_seconds     = level->time;
4352   cav->gems_needed      = level->gems_needed;
4353
4354   cav->emerald_score    = level->score[SC_EMERALD];
4355   cav->diamond_score    = level->score[SC_DIAMOND];
4356   cav->alien_score      = level->score[SC_ROBOT];
4357   cav->tank_score       = level->score[SC_SPACESHIP];
4358   cav->bug_score        = level->score[SC_BUG];
4359   cav->eater_score      = level->score[SC_YAMYAM];
4360   cav->nut_score        = level->score[SC_NUT];
4361   cav->dynamite_score   = level->score[SC_DYNAMITE];
4362   cav->key_score        = level->score[SC_KEY];
4363   cav->exit_score       = level->score[SC_TIME_BONUS];
4364
4365   cav->num_eater_arrays = level->num_yamyam_contents;
4366
4367   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4368     for (y = 0; y < 3; y++)
4369       for (x = 0; x < 3; x++)
4370         cav->eater_array[i][y * 3 + x] =
4371           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4372
4373   cav->amoeba_time              = level->amoeba_speed;
4374   cav->wonderwall_time          = level->time_magic_wall;
4375   cav->wheel_time               = level->time_wheel;
4376
4377   cav->android_move_time        = level->android_move_time;
4378   cav->android_clone_time       = level->android_clone_time;
4379   cav->ball_random              = level->ball_random;
4380   cav->ball_active              = level->ball_active_initial;
4381   cav->ball_time                = level->ball_time;
4382   cav->num_ball_arrays          = level->num_ball_contents;
4383
4384   cav->lenses_score             = level->lenses_score;
4385   cav->magnify_score            = level->magnify_score;
4386   cav->slurp_score              = level->slurp_score;
4387
4388   cav->lenses_time              = level->lenses_time;
4389   cav->magnify_time             = level->magnify_time;
4390
4391   cav->wind_time = 9999;
4392   cav->wind_direction =
4393     map_direction_RND_to_EM(level->wind_direction_initial);
4394
4395   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4396     for (j = 0; j < 8; j++)
4397       cav->ball_array[i][j] =
4398         map_element_RND_to_EM_cave(level->ball_content[i].
4399                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4400
4401   map_android_clone_elements_RND_to_EM(level);
4402
4403   // first fill the complete playfield with the empty space element
4404   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4405     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4406       cav->cave[x][y] = Cblank;
4407
4408   // then copy the real level contents from level file into the playfield
4409   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4410   {
4411     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4412
4413     if (level->field[x][y] == EL_AMOEBA_DEAD)
4414       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4415
4416     cav->cave[x][y] = new_element;
4417   }
4418
4419   for (i = 0; i < MAX_PLAYERS; i++)
4420   {
4421     cav->player_x[i] = -1;
4422     cav->player_y[i] = -1;
4423   }
4424
4425   // initialize player positions and delete players from the playfield
4426   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4427   {
4428     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4429     {
4430       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4431
4432       cav->player_x[player_nr] = x;
4433       cav->player_y[player_nr] = y;
4434
4435       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4436     }
4437   }
4438 }
4439
4440 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4441 {
4442   static int ball_xy[8][2] =
4443   {
4444     { 0, 0 },
4445     { 1, 0 },
4446     { 2, 0 },
4447     { 0, 1 },
4448     { 2, 1 },
4449     { 0, 2 },
4450     { 1, 2 },
4451     { 2, 2 },
4452   };
4453   struct LevelInfo_EM *level_em = level->native_em_level;
4454   struct CAVE *cav = level_em->cav;
4455   int i, j, x, y;
4456
4457   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4458   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4459
4460   level->time        = cav->time_seconds;
4461   level->gems_needed = cav->gems_needed;
4462
4463   sprintf(level->name, "Level %d", level->file_info.nr);
4464
4465   level->score[SC_EMERALD]      = cav->emerald_score;
4466   level->score[SC_DIAMOND]      = cav->diamond_score;
4467   level->score[SC_ROBOT]        = cav->alien_score;
4468   level->score[SC_SPACESHIP]    = cav->tank_score;
4469   level->score[SC_BUG]          = cav->bug_score;
4470   level->score[SC_YAMYAM]       = cav->eater_score;
4471   level->score[SC_NUT]          = cav->nut_score;
4472   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4473   level->score[SC_KEY]          = cav->key_score;
4474   level->score[SC_TIME_BONUS]   = cav->exit_score;
4475
4476   level->num_yamyam_contents    = cav->num_eater_arrays;
4477
4478   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4479     for (y = 0; y < 3; y++)
4480       for (x = 0; x < 3; x++)
4481         level->yamyam_content[i].e[x][y] =
4482           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4483
4484   level->amoeba_speed           = cav->amoeba_time;
4485   level->time_magic_wall        = cav->wonderwall_time;
4486   level->time_wheel             = cav->wheel_time;
4487
4488   level->android_move_time      = cav->android_move_time;
4489   level->android_clone_time     = cav->android_clone_time;
4490   level->ball_random            = cav->ball_random;
4491   level->ball_active_initial    = cav->ball_active;
4492   level->ball_time              = cav->ball_time;
4493   level->num_ball_contents      = cav->num_ball_arrays;
4494
4495   level->lenses_score           = cav->lenses_score;
4496   level->magnify_score          = cav->magnify_score;
4497   level->slurp_score            = cav->slurp_score;
4498
4499   level->lenses_time            = cav->lenses_time;
4500   level->magnify_time           = cav->magnify_time;
4501
4502   level->wind_direction_initial =
4503     map_direction_EM_to_RND(cav->wind_direction);
4504
4505   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4506     for (j = 0; j < 8; j++)
4507       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4508         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4509
4510   map_android_clone_elements_EM_to_RND(level);
4511
4512   // convert the playfield (some elements need special treatment)
4513   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4514   {
4515     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4516
4517     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4518       new_element = EL_AMOEBA_DEAD;
4519
4520     level->field[x][y] = new_element;
4521   }
4522
4523   for (i = 0; i < MAX_PLAYERS; i++)
4524   {
4525     // in case of all players set to the same field, use the first player
4526     int nr = MAX_PLAYERS - i - 1;
4527     int jx = cav->player_x[nr];
4528     int jy = cav->player_y[nr];
4529
4530     if (jx != -1 && jy != -1)
4531       level->field[jx][jy] = EL_PLAYER_1 + nr;
4532   }
4533
4534   // time score is counted for each 10 seconds left in Emerald Mine levels
4535   level->time_score_base = 10;
4536 }
4537
4538
4539 // ----------------------------------------------------------------------------
4540 // functions for loading SP level
4541 // ----------------------------------------------------------------------------
4542
4543 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4544 {
4545   struct LevelInfo_SP *level_sp = level->native_sp_level;
4546   LevelInfoType *header = &level_sp->header;
4547   int i, x, y;
4548
4549   level_sp->width  = level->fieldx;
4550   level_sp->height = level->fieldy;
4551
4552   for (x = 0; x < level->fieldx; x++)
4553     for (y = 0; y < level->fieldy; y++)
4554       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4555
4556   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4557
4558   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4559     header->LevelTitle[i] = level->name[i];
4560   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4561
4562   header->InfotronsNeeded = level->gems_needed;
4563
4564   header->SpecialPortCount = 0;
4565
4566   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4567   {
4568     boolean gravity_port_found = FALSE;
4569     boolean gravity_port_valid = FALSE;
4570     int gravity_port_flag;
4571     int gravity_port_base_element;
4572     int element = level->field[x][y];
4573
4574     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4575         element <= EL_SP_GRAVITY_ON_PORT_UP)
4576     {
4577       gravity_port_found = TRUE;
4578       gravity_port_valid = TRUE;
4579       gravity_port_flag = 1;
4580       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4581     }
4582     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4583              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4584     {
4585       gravity_port_found = TRUE;
4586       gravity_port_valid = TRUE;
4587       gravity_port_flag = 0;
4588       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4589     }
4590     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4591              element <= EL_SP_GRAVITY_PORT_UP)
4592     {
4593       // change R'n'D style gravity inverting special port to normal port
4594       // (there are no gravity inverting ports in native Supaplex engine)
4595
4596       gravity_port_found = TRUE;
4597       gravity_port_valid = FALSE;
4598       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4599     }
4600
4601     if (gravity_port_found)
4602     {
4603       if (gravity_port_valid &&
4604           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4605       {
4606         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4607
4608         port->PortLocation = (y * level->fieldx + x) * 2;
4609         port->Gravity = gravity_port_flag;
4610
4611         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4612
4613         header->SpecialPortCount++;
4614       }
4615       else
4616       {
4617         // change special gravity port to normal port
4618
4619         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4620       }
4621
4622       level_sp->playfield[x][y] = element - EL_SP_START;
4623     }
4624   }
4625 }
4626
4627 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4628 {
4629   struct LevelInfo_SP *level_sp = level->native_sp_level;
4630   LevelInfoType *header = &level_sp->header;
4631   boolean num_invalid_elements = 0;
4632   int i, j, x, y;
4633
4634   level->fieldx = level_sp->width;
4635   level->fieldy = level_sp->height;
4636
4637   for (x = 0; x < level->fieldx; x++)
4638   {
4639     for (y = 0; y < level->fieldy; y++)
4640     {
4641       int element_old = level_sp->playfield[x][y];
4642       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4643
4644       if (element_new == EL_UNKNOWN)
4645       {
4646         num_invalid_elements++;
4647
4648         Debug("level:native:SP", "invalid element %d at position %d, %d",
4649               element_old, x, y);
4650       }
4651
4652       level->field[x][y] = element_new;
4653     }
4654   }
4655
4656   if (num_invalid_elements > 0)
4657     Warn("found %d invalid elements%s", num_invalid_elements,
4658          (!options.debug ? " (use '--debug' for more details)" : ""));
4659
4660   for (i = 0; i < MAX_PLAYERS; i++)
4661     level->initial_player_gravity[i] =
4662       (header->InitialGravity == 1 ? TRUE : FALSE);
4663
4664   // skip leading spaces
4665   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4666     if (header->LevelTitle[i] != ' ')
4667       break;
4668
4669   // copy level title
4670   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4671     level->name[j] = header->LevelTitle[i];
4672   level->name[j] = '\0';
4673
4674   // cut trailing spaces
4675   for (; j > 0; j--)
4676     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4677       level->name[j - 1] = '\0';
4678
4679   level->gems_needed = header->InfotronsNeeded;
4680
4681   for (i = 0; i < header->SpecialPortCount; i++)
4682   {
4683     SpecialPortType *port = &header->SpecialPort[i];
4684     int port_location = port->PortLocation;
4685     int gravity = port->Gravity;
4686     int port_x, port_y, port_element;
4687
4688     port_x = (port_location / 2) % level->fieldx;
4689     port_y = (port_location / 2) / level->fieldx;
4690
4691     if (port_x < 0 || port_x >= level->fieldx ||
4692         port_y < 0 || port_y >= level->fieldy)
4693     {
4694       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4695
4696       continue;
4697     }
4698
4699     port_element = level->field[port_x][port_y];
4700
4701     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4702         port_element > EL_SP_GRAVITY_PORT_UP)
4703     {
4704       Warn("no special port at position (%d, %d)", port_x, port_y);
4705
4706       continue;
4707     }
4708
4709     // change previous (wrong) gravity inverting special port to either
4710     // gravity enabling special port or gravity disabling special port
4711     level->field[port_x][port_y] +=
4712       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4713        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4714   }
4715
4716   // change special gravity ports without database entries to normal ports
4717   for (x = 0; x < level->fieldx; x++)
4718     for (y = 0; y < level->fieldy; y++)
4719       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4720           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4721         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4722
4723   level->time = 0;                      // no time limit
4724   level->amoeba_speed = 0;
4725   level->time_magic_wall = 0;
4726   level->time_wheel = 0;
4727   level->amoeba_content = EL_EMPTY;
4728
4729   // original Supaplex does not use score values -- rate by playing time
4730   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4731     level->score[i] = 0;
4732
4733   level->rate_time_over_score = TRUE;
4734
4735   // there are no yamyams in supaplex levels
4736   for (i = 0; i < level->num_yamyam_contents; i++)
4737     for (x = 0; x < 3; x++)
4738       for (y = 0; y < 3; y++)
4739         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4740 }
4741
4742 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4743 {
4744   struct LevelInfo_SP *level_sp = level->native_sp_level;
4745   struct DemoInfo_SP *demo = &level_sp->demo;
4746   int i, j;
4747
4748   // always start with reliable default values
4749   demo->is_available = FALSE;
4750   demo->length = 0;
4751
4752   if (TAPE_IS_EMPTY(tape))
4753     return;
4754
4755   demo->level_nr = tape.level_nr;       // (currently not used)
4756
4757   level_sp->header.DemoRandomSeed = tape.random_seed;
4758
4759   demo->length = 0;
4760
4761   for (i = 0; i < tape.length; i++)
4762   {
4763     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4764     int demo_repeat = tape.pos[i].delay;
4765     int demo_entries = (demo_repeat + 15) / 16;
4766
4767     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4768     {
4769       Warn("tape truncated: size exceeds maximum SP demo size %d",
4770            SP_MAX_TAPE_LEN);
4771
4772       break;
4773     }
4774
4775     for (j = 0; j < demo_repeat / 16; j++)
4776       demo->data[demo->length++] = 0xf0 | demo_action;
4777
4778     if (demo_repeat % 16)
4779       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4780   }
4781
4782   demo->is_available = TRUE;
4783 }
4784
4785 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4786 {
4787   struct LevelInfo_SP *level_sp = level->native_sp_level;
4788   struct DemoInfo_SP *demo = &level_sp->demo;
4789   char *filename = level->file_info.filename;
4790   int i;
4791
4792   // always start with reliable default values
4793   setTapeInfoToDefaults();
4794
4795   if (!demo->is_available)
4796     return;
4797
4798   tape.level_nr = demo->level_nr;       // (currently not used)
4799   tape.random_seed = level_sp->header.DemoRandomSeed;
4800
4801   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4802
4803   tape.counter = 0;
4804   tape.pos[tape.counter].delay = 0;
4805
4806   for (i = 0; i < demo->length; i++)
4807   {
4808     int demo_action = demo->data[i] & 0x0f;
4809     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4810     int tape_action = map_key_SP_to_RND(demo_action);
4811     int tape_repeat = demo_repeat + 1;
4812     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4813     boolean success = 0;
4814     int j;
4815
4816     for (j = 0; j < tape_repeat; j++)
4817       success = TapeAddAction(action);
4818
4819     if (!success)
4820     {
4821       Warn("SP demo truncated: size exceeds maximum tape size %d",
4822            MAX_TAPE_LEN);
4823
4824       break;
4825     }
4826   }
4827
4828   TapeHaltRecording();
4829 }
4830
4831
4832 // ----------------------------------------------------------------------------
4833 // functions for loading MM level
4834 // ----------------------------------------------------------------------------
4835
4836 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4837 {
4838   struct LevelInfo_MM *level_mm = level->native_mm_level;
4839   int i, x, y;
4840
4841   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4842   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4843
4844   level_mm->time = level->time;
4845   level_mm->kettles_needed = level->gems_needed;
4846   level_mm->auto_count_kettles = level->auto_count_gems;
4847
4848   level_mm->mm_laser_red   = level->mm_laser_red;
4849   level_mm->mm_laser_green = level->mm_laser_green;
4850   level_mm->mm_laser_blue  = level->mm_laser_blue;
4851
4852   level_mm->df_laser_red   = level->df_laser_red;
4853   level_mm->df_laser_green = level->df_laser_green;
4854   level_mm->df_laser_blue  = level->df_laser_blue;
4855
4856   strcpy(level_mm->name, level->name);
4857   strcpy(level_mm->author, level->author);
4858
4859   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4860   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4861   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4862   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4863   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4864
4865   level_mm->amoeba_speed = level->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_mm->num_ball_contents = level->num_mm_ball_contents;
4872   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4873   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4874   level_mm->explode_ball = level->explode_mm_ball;
4875
4876   for (i = 0; i < level->num_mm_ball_contents; i++)
4877     level_mm->ball_content[i] =
4878       map_element_RND_to_MM(level->mm_ball_content[i]);
4879
4880   for (x = 0; x < level->fieldx; x++)
4881     for (y = 0; y < level->fieldy; y++)
4882       Ur[x][y] =
4883         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4884 }
4885
4886 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4887 {
4888   struct LevelInfo_MM *level_mm = level->native_mm_level;
4889   int i, x, y;
4890
4891   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4892   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4893
4894   level->time = level_mm->time;
4895   level->gems_needed = level_mm->kettles_needed;
4896   level->auto_count_gems = level_mm->auto_count_kettles;
4897
4898   level->mm_laser_red   = level_mm->mm_laser_red;
4899   level->mm_laser_green = level_mm->mm_laser_green;
4900   level->mm_laser_blue  = level_mm->mm_laser_blue;
4901
4902   level->df_laser_red   = level_mm->df_laser_red;
4903   level->df_laser_green = level_mm->df_laser_green;
4904   level->df_laser_blue  = level_mm->df_laser_blue;
4905
4906   strcpy(level->name, level_mm->name);
4907
4908   // only overwrite author from 'levelinfo.conf' if author defined in level
4909   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4910     strcpy(level->author, level_mm->author);
4911
4912   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4913   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4914   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4915   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4916   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4917
4918   level->amoeba_speed  = level_mm->amoeba_speed;
4919   level->mm_time_fuse  = level_mm->time_fuse;
4920   level->mm_time_bomb  = level_mm->time_bomb;
4921   level->mm_time_ball  = level_mm->time_ball;
4922   level->mm_time_block = level_mm->time_block;
4923
4924   level->num_mm_ball_contents = level_mm->num_ball_contents;
4925   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4926   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4927   level->explode_mm_ball = level_mm->explode_ball;
4928
4929   for (i = 0; i < level->num_mm_ball_contents; i++)
4930     level->mm_ball_content[i] =
4931       map_element_MM_to_RND(level_mm->ball_content[i]);
4932
4933   for (x = 0; x < level->fieldx; x++)
4934     for (y = 0; y < level->fieldy; y++)
4935       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4936 }
4937
4938
4939 // ----------------------------------------------------------------------------
4940 // functions for loading DC level
4941 // ----------------------------------------------------------------------------
4942
4943 #define DC_LEVEL_HEADER_SIZE            344
4944
4945 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4946                                         boolean init)
4947 {
4948   static int last_data_encoded;
4949   static int offset1;
4950   static int offset2;
4951   int diff;
4952   int diff_hi, diff_lo;
4953   int data_hi, data_lo;
4954   unsigned short data_decoded;
4955
4956   if (init)
4957   {
4958     last_data_encoded = 0;
4959     offset1 = -1;
4960     offset2 = 0;
4961
4962     return 0;
4963   }
4964
4965   diff = data_encoded - last_data_encoded;
4966   diff_hi = diff & ~0xff;
4967   diff_lo = diff &  0xff;
4968
4969   offset2 += diff_lo;
4970
4971   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4972   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4973   data_hi = data_hi & 0xff00;
4974
4975   data_decoded = data_hi | data_lo;
4976
4977   last_data_encoded = data_encoded;
4978
4979   offset1 = (offset1 + 1) % 31;
4980   offset2 = offset2 & 0xff;
4981
4982   return data_decoded;
4983 }
4984
4985 static int getMappedElement_DC(int element)
4986 {
4987   switch (element)
4988   {
4989     case 0x0000:
4990       element = EL_ROCK;
4991       break;
4992
4993       // 0x0117 - 0x036e: (?)
4994       // EL_DIAMOND
4995
4996       // 0x042d - 0x0684: (?)
4997       // EL_EMERALD
4998
4999     case 0x06f1:
5000       element = EL_NUT;
5001       break;
5002
5003     case 0x074c:
5004       element = EL_BOMB;
5005       break;
5006
5007     case 0x07a4:
5008       element = EL_PEARL;
5009       break;
5010
5011     case 0x0823:
5012       element = EL_CRYSTAL;
5013       break;
5014
5015     case 0x0e77:        // quicksand (boulder)
5016       element = EL_QUICKSAND_FAST_FULL;
5017       break;
5018
5019     case 0x0e99:        // slow quicksand (boulder)
5020       element = EL_QUICKSAND_FULL;
5021       break;
5022
5023     case 0x0ed2:
5024       element = EL_EM_EXIT_OPEN;
5025       break;
5026
5027     case 0x0ee3:
5028       element = EL_EM_EXIT_CLOSED;
5029       break;
5030
5031     case 0x0eeb:
5032       element = EL_EM_STEEL_EXIT_OPEN;
5033       break;
5034
5035     case 0x0efc:
5036       element = EL_EM_STEEL_EXIT_CLOSED;
5037       break;
5038
5039     case 0x0f4f:        // dynamite (lit 1)
5040       element = EL_EM_DYNAMITE_ACTIVE;
5041       break;
5042
5043     case 0x0f57:        // dynamite (lit 2)
5044       element = EL_EM_DYNAMITE_ACTIVE;
5045       break;
5046
5047     case 0x0f5f:        // dynamite (lit 3)
5048       element = EL_EM_DYNAMITE_ACTIVE;
5049       break;
5050
5051     case 0x0f67:        // dynamite (lit 4)
5052       element = EL_EM_DYNAMITE_ACTIVE;
5053       break;
5054
5055     case 0x0f81:
5056     case 0x0f82:
5057     case 0x0f83:
5058     case 0x0f84:
5059       element = EL_AMOEBA_WET;
5060       break;
5061
5062     case 0x0f85:
5063       element = EL_AMOEBA_DROP;
5064       break;
5065
5066     case 0x0fb9:
5067       element = EL_DC_MAGIC_WALL;
5068       break;
5069
5070     case 0x0fd0:
5071       element = EL_SPACESHIP_UP;
5072       break;
5073
5074     case 0x0fd9:
5075       element = EL_SPACESHIP_DOWN;
5076       break;
5077
5078     case 0x0ff1:
5079       element = EL_SPACESHIP_LEFT;
5080       break;
5081
5082     case 0x0ff9:
5083       element = EL_SPACESHIP_RIGHT;
5084       break;
5085
5086     case 0x1057:
5087       element = EL_BUG_UP;
5088       break;
5089
5090     case 0x1060:
5091       element = EL_BUG_DOWN;
5092       break;
5093
5094     case 0x1078:
5095       element = EL_BUG_LEFT;
5096       break;
5097
5098     case 0x1080:
5099       element = EL_BUG_RIGHT;
5100       break;
5101
5102     case 0x10de:
5103       element = EL_MOLE_UP;
5104       break;
5105
5106     case 0x10e7:
5107       element = EL_MOLE_DOWN;
5108       break;
5109
5110     case 0x10ff:
5111       element = EL_MOLE_LEFT;
5112       break;
5113
5114     case 0x1107:
5115       element = EL_MOLE_RIGHT;
5116       break;
5117
5118     case 0x11c0:
5119       element = EL_ROBOT;
5120       break;
5121
5122     case 0x13f5:
5123       element = EL_YAMYAM_UP;
5124       break;
5125
5126     case 0x1425:
5127       element = EL_SWITCHGATE_OPEN;
5128       break;
5129
5130     case 0x1426:
5131       element = EL_SWITCHGATE_CLOSED;
5132       break;
5133
5134     case 0x1437:
5135       element = EL_DC_SWITCHGATE_SWITCH_UP;
5136       break;
5137
5138     case 0x143a:
5139       element = EL_TIMEGATE_CLOSED;
5140       break;
5141
5142     case 0x144c:        // conveyor belt switch (green)
5143       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5144       break;
5145
5146     case 0x144f:        // conveyor belt switch (red)
5147       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5148       break;
5149
5150     case 0x1452:        // conveyor belt switch (blue)
5151       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5152       break;
5153
5154     case 0x145b:
5155       element = EL_CONVEYOR_BELT_3_MIDDLE;
5156       break;
5157
5158     case 0x1463:
5159       element = EL_CONVEYOR_BELT_3_LEFT;
5160       break;
5161
5162     case 0x146b:
5163       element = EL_CONVEYOR_BELT_3_RIGHT;
5164       break;
5165
5166     case 0x1473:
5167       element = EL_CONVEYOR_BELT_1_MIDDLE;
5168       break;
5169
5170     case 0x147b:
5171       element = EL_CONVEYOR_BELT_1_LEFT;
5172       break;
5173
5174     case 0x1483:
5175       element = EL_CONVEYOR_BELT_1_RIGHT;
5176       break;
5177
5178     case 0x148b:
5179       element = EL_CONVEYOR_BELT_4_MIDDLE;
5180       break;
5181
5182     case 0x1493:
5183       element = EL_CONVEYOR_BELT_4_LEFT;
5184       break;
5185
5186     case 0x149b:
5187       element = EL_CONVEYOR_BELT_4_RIGHT;
5188       break;
5189
5190     case 0x14ac:
5191       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5192       break;
5193
5194     case 0x14bd:
5195       element = EL_EXPANDABLE_WALL_VERTICAL;
5196       break;
5197
5198     case 0x14c6:
5199       element = EL_EXPANDABLE_WALL_ANY;
5200       break;
5201
5202     case 0x14ce:        // growing steel wall (left/right)
5203       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5204       break;
5205
5206     case 0x14df:        // growing steel wall (up/down)
5207       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5208       break;
5209
5210     case 0x14e8:        // growing steel wall (up/down/left/right)
5211       element = EL_EXPANDABLE_STEELWALL_ANY;
5212       break;
5213
5214     case 0x14e9:
5215       element = EL_SHIELD_DEADLY;
5216       break;
5217
5218     case 0x1501:
5219       element = EL_EXTRA_TIME;
5220       break;
5221
5222     case 0x154f:
5223       element = EL_ACID;
5224       break;
5225
5226     case 0x1577:
5227       element = EL_EMPTY_SPACE;
5228       break;
5229
5230     case 0x1578:        // quicksand (empty)
5231       element = EL_QUICKSAND_FAST_EMPTY;
5232       break;
5233
5234     case 0x1579:        // slow quicksand (empty)
5235       element = EL_QUICKSAND_EMPTY;
5236       break;
5237
5238       // 0x157c - 0x158b:
5239       // EL_SAND
5240
5241       // 0x1590 - 0x159f:
5242       // EL_DC_LANDMINE
5243
5244     case 0x15a0:
5245       element = EL_EM_DYNAMITE;
5246       break;
5247
5248     case 0x15a1:        // key (red)
5249       element = EL_EM_KEY_1;
5250       break;
5251
5252     case 0x15a2:        // key (yellow)
5253       element = EL_EM_KEY_2;
5254       break;
5255
5256     case 0x15a3:        // key (blue)
5257       element = EL_EM_KEY_4;
5258       break;
5259
5260     case 0x15a4:        // key (green)
5261       element = EL_EM_KEY_3;
5262       break;
5263
5264     case 0x15a5:        // key (white)
5265       element = EL_DC_KEY_WHITE;
5266       break;
5267
5268     case 0x15a6:
5269       element = EL_WALL_SLIPPERY;
5270       break;
5271
5272     case 0x15a7:
5273       element = EL_WALL;
5274       break;
5275
5276     case 0x15a8:        // wall (not round)
5277       element = EL_WALL;
5278       break;
5279
5280     case 0x15a9:        // (blue)
5281       element = EL_CHAR_A;
5282       break;
5283
5284     case 0x15aa:        // (blue)
5285       element = EL_CHAR_B;
5286       break;
5287
5288     case 0x15ab:        // (blue)
5289       element = EL_CHAR_C;
5290       break;
5291
5292     case 0x15ac:        // (blue)
5293       element = EL_CHAR_D;
5294       break;
5295
5296     case 0x15ad:        // (blue)
5297       element = EL_CHAR_E;
5298       break;
5299
5300     case 0x15ae:        // (blue)
5301       element = EL_CHAR_F;
5302       break;
5303
5304     case 0x15af:        // (blue)
5305       element = EL_CHAR_G;
5306       break;
5307
5308     case 0x15b0:        // (blue)
5309       element = EL_CHAR_H;
5310       break;
5311
5312     case 0x15b1:        // (blue)
5313       element = EL_CHAR_I;
5314       break;
5315
5316     case 0x15b2:        // (blue)
5317       element = EL_CHAR_J;
5318       break;
5319
5320     case 0x15b3:        // (blue)
5321       element = EL_CHAR_K;
5322       break;
5323
5324     case 0x15b4:        // (blue)
5325       element = EL_CHAR_L;
5326       break;
5327
5328     case 0x15b5:        // (blue)
5329       element = EL_CHAR_M;
5330       break;
5331
5332     case 0x15b6:        // (blue)
5333       element = EL_CHAR_N;
5334       break;
5335
5336     case 0x15b7:        // (blue)
5337       element = EL_CHAR_O;
5338       break;
5339
5340     case 0x15b8:        // (blue)
5341       element = EL_CHAR_P;
5342       break;
5343
5344     case 0x15b9:        // (blue)
5345       element = EL_CHAR_Q;
5346       break;
5347
5348     case 0x15ba:        // (blue)
5349       element = EL_CHAR_R;
5350       break;
5351
5352     case 0x15bb:        // (blue)
5353       element = EL_CHAR_S;
5354       break;
5355
5356     case 0x15bc:        // (blue)
5357       element = EL_CHAR_T;
5358       break;
5359
5360     case 0x15bd:        // (blue)
5361       element = EL_CHAR_U;
5362       break;
5363
5364     case 0x15be:        // (blue)
5365       element = EL_CHAR_V;
5366       break;
5367
5368     case 0x15bf:        // (blue)
5369       element = EL_CHAR_W;
5370       break;
5371
5372     case 0x15c0:        // (blue)
5373       element = EL_CHAR_X;
5374       break;
5375
5376     case 0x15c1:        // (blue)
5377       element = EL_CHAR_Y;
5378       break;
5379
5380     case 0x15c2:        // (blue)
5381       element = EL_CHAR_Z;
5382       break;
5383
5384     case 0x15c3:        // (blue)
5385       element = EL_CHAR_AUMLAUT;
5386       break;
5387
5388     case 0x15c4:        // (blue)
5389       element = EL_CHAR_OUMLAUT;
5390       break;
5391
5392     case 0x15c5:        // (blue)
5393       element = EL_CHAR_UUMLAUT;
5394       break;
5395
5396     case 0x15c6:        // (blue)
5397       element = EL_CHAR_0;
5398       break;
5399
5400     case 0x15c7:        // (blue)
5401       element = EL_CHAR_1;
5402       break;
5403
5404     case 0x15c8:        // (blue)
5405       element = EL_CHAR_2;
5406       break;
5407
5408     case 0x15c9:        // (blue)
5409       element = EL_CHAR_3;
5410       break;
5411
5412     case 0x15ca:        // (blue)
5413       element = EL_CHAR_4;
5414       break;
5415
5416     case 0x15cb:        // (blue)
5417       element = EL_CHAR_5;
5418       break;
5419
5420     case 0x15cc:        // (blue)
5421       element = EL_CHAR_6;
5422       break;
5423
5424     case 0x15cd:        // (blue)
5425       element = EL_CHAR_7;
5426       break;
5427
5428     case 0x15ce:        // (blue)
5429       element = EL_CHAR_8;
5430       break;
5431
5432     case 0x15cf:        // (blue)
5433       element = EL_CHAR_9;
5434       break;
5435
5436     case 0x15d0:        // (blue)
5437       element = EL_CHAR_PERIOD;
5438       break;
5439
5440     case 0x15d1:        // (blue)
5441       element = EL_CHAR_EXCLAM;
5442       break;
5443
5444     case 0x15d2:        // (blue)
5445       element = EL_CHAR_COLON;
5446       break;
5447
5448     case 0x15d3:        // (blue)
5449       element = EL_CHAR_LESS;
5450       break;
5451
5452     case 0x15d4:        // (blue)
5453       element = EL_CHAR_GREATER;
5454       break;
5455
5456     case 0x15d5:        // (blue)
5457       element = EL_CHAR_QUESTION;
5458       break;
5459
5460     case 0x15d6:        // (blue)
5461       element = EL_CHAR_COPYRIGHT;
5462       break;
5463
5464     case 0x15d7:        // (blue)
5465       element = EL_CHAR_UP;
5466       break;
5467
5468     case 0x15d8:        // (blue)
5469       element = EL_CHAR_DOWN;
5470       break;
5471
5472     case 0x15d9:        // (blue)
5473       element = EL_CHAR_BUTTON;
5474       break;
5475
5476     case 0x15da:        // (blue)
5477       element = EL_CHAR_PLUS;
5478       break;
5479
5480     case 0x15db:        // (blue)
5481       element = EL_CHAR_MINUS;
5482       break;
5483
5484     case 0x15dc:        // (blue)
5485       element = EL_CHAR_APOSTROPHE;
5486       break;
5487
5488     case 0x15dd:        // (blue)
5489       element = EL_CHAR_PARENLEFT;
5490       break;
5491
5492     case 0x15de:        // (blue)
5493       element = EL_CHAR_PARENRIGHT;
5494       break;
5495
5496     case 0x15df:        // (green)
5497       element = EL_CHAR_A;
5498       break;
5499
5500     case 0x15e0:        // (green)
5501       element = EL_CHAR_B;
5502       break;
5503
5504     case 0x15e1:        // (green)
5505       element = EL_CHAR_C;
5506       break;
5507
5508     case 0x15e2:        // (green)
5509       element = EL_CHAR_D;
5510       break;
5511
5512     case 0x15e3:        // (green)
5513       element = EL_CHAR_E;
5514       break;
5515
5516     case 0x15e4:        // (green)
5517       element = EL_CHAR_F;
5518       break;
5519
5520     case 0x15e5:        // (green)
5521       element = EL_CHAR_G;
5522       break;
5523
5524     case 0x15e6:        // (green)
5525       element = EL_CHAR_H;
5526       break;
5527
5528     case 0x15e7:        // (green)
5529       element = EL_CHAR_I;
5530       break;
5531
5532     case 0x15e8:        // (green)
5533       element = EL_CHAR_J;
5534       break;
5535
5536     case 0x15e9:        // (green)
5537       element = EL_CHAR_K;
5538       break;
5539
5540     case 0x15ea:        // (green)
5541       element = EL_CHAR_L;
5542       break;
5543
5544     case 0x15eb:        // (green)
5545       element = EL_CHAR_M;
5546       break;
5547
5548     case 0x15ec:        // (green)
5549       element = EL_CHAR_N;
5550       break;
5551
5552     case 0x15ed:        // (green)
5553       element = EL_CHAR_O;
5554       break;
5555
5556     case 0x15ee:        // (green)
5557       element = EL_CHAR_P;
5558       break;
5559
5560     case 0x15ef:        // (green)
5561       element = EL_CHAR_Q;
5562       break;
5563
5564     case 0x15f0:        // (green)
5565       element = EL_CHAR_R;
5566       break;
5567
5568     case 0x15f1:        // (green)
5569       element = EL_CHAR_S;
5570       break;
5571
5572     case 0x15f2:        // (green)
5573       element = EL_CHAR_T;
5574       break;
5575
5576     case 0x15f3:        // (green)
5577       element = EL_CHAR_U;
5578       break;
5579
5580     case 0x15f4:        // (green)
5581       element = EL_CHAR_V;
5582       break;
5583
5584     case 0x15f5:        // (green)
5585       element = EL_CHAR_W;
5586       break;
5587
5588     case 0x15f6:        // (green)
5589       element = EL_CHAR_X;
5590       break;
5591
5592     case 0x15f7:        // (green)
5593       element = EL_CHAR_Y;
5594       break;
5595
5596     case 0x15f8:        // (green)
5597       element = EL_CHAR_Z;
5598       break;
5599
5600     case 0x15f9:        // (green)
5601       element = EL_CHAR_AUMLAUT;
5602       break;
5603
5604     case 0x15fa:        // (green)
5605       element = EL_CHAR_OUMLAUT;
5606       break;
5607
5608     case 0x15fb:        // (green)
5609       element = EL_CHAR_UUMLAUT;
5610       break;
5611
5612     case 0x15fc:        // (green)
5613       element = EL_CHAR_0;
5614       break;
5615
5616     case 0x15fd:        // (green)
5617       element = EL_CHAR_1;
5618       break;
5619
5620     case 0x15fe:        // (green)
5621       element = EL_CHAR_2;
5622       break;
5623
5624     case 0x15ff:        // (green)
5625       element = EL_CHAR_3;
5626       break;
5627
5628     case 0x1600:        // (green)
5629       element = EL_CHAR_4;
5630       break;
5631
5632     case 0x1601:        // (green)
5633       element = EL_CHAR_5;
5634       break;
5635
5636     case 0x1602:        // (green)
5637       element = EL_CHAR_6;
5638       break;
5639
5640     case 0x1603:        // (green)
5641       element = EL_CHAR_7;
5642       break;
5643
5644     case 0x1604:        // (green)
5645       element = EL_CHAR_8;
5646       break;
5647
5648     case 0x1605:        // (green)
5649       element = EL_CHAR_9;
5650       break;
5651
5652     case 0x1606:        // (green)
5653       element = EL_CHAR_PERIOD;
5654       break;
5655
5656     case 0x1607:        // (green)
5657       element = EL_CHAR_EXCLAM;
5658       break;
5659
5660     case 0x1608:        // (green)
5661       element = EL_CHAR_COLON;
5662       break;
5663
5664     case 0x1609:        // (green)
5665       element = EL_CHAR_LESS;
5666       break;
5667
5668     case 0x160a:        // (green)
5669       element = EL_CHAR_GREATER;
5670       break;
5671
5672     case 0x160b:        // (green)
5673       element = EL_CHAR_QUESTION;
5674       break;
5675
5676     case 0x160c:        // (green)
5677       element = EL_CHAR_COPYRIGHT;
5678       break;
5679
5680     case 0x160d:        // (green)
5681       element = EL_CHAR_UP;
5682       break;
5683
5684     case 0x160e:        // (green)
5685       element = EL_CHAR_DOWN;
5686       break;
5687
5688     case 0x160f:        // (green)
5689       element = EL_CHAR_BUTTON;
5690       break;
5691
5692     case 0x1610:        // (green)
5693       element = EL_CHAR_PLUS;
5694       break;
5695
5696     case 0x1611:        // (green)
5697       element = EL_CHAR_MINUS;
5698       break;
5699
5700     case 0x1612:        // (green)
5701       element = EL_CHAR_APOSTROPHE;
5702       break;
5703
5704     case 0x1613:        // (green)
5705       element = EL_CHAR_PARENLEFT;
5706       break;
5707
5708     case 0x1614:        // (green)
5709       element = EL_CHAR_PARENRIGHT;
5710       break;
5711
5712     case 0x1615:        // (blue steel)
5713       element = EL_STEEL_CHAR_A;
5714       break;
5715
5716     case 0x1616:        // (blue steel)
5717       element = EL_STEEL_CHAR_B;
5718       break;
5719
5720     case 0x1617:        // (blue steel)
5721       element = EL_STEEL_CHAR_C;
5722       break;
5723
5724     case 0x1618:        // (blue steel)
5725       element = EL_STEEL_CHAR_D;
5726       break;
5727
5728     case 0x1619:        // (blue steel)
5729       element = EL_STEEL_CHAR_E;
5730       break;
5731
5732     case 0x161a:        // (blue steel)
5733       element = EL_STEEL_CHAR_F;
5734       break;
5735
5736     case 0x161b:        // (blue steel)
5737       element = EL_STEEL_CHAR_G;
5738       break;
5739
5740     case 0x161c:        // (blue steel)
5741       element = EL_STEEL_CHAR_H;
5742       break;
5743
5744     case 0x161d:        // (blue steel)
5745       element = EL_STEEL_CHAR_I;
5746       break;
5747
5748     case 0x161e:        // (blue steel)
5749       element = EL_STEEL_CHAR_J;
5750       break;
5751
5752     case 0x161f:        // (blue steel)
5753       element = EL_STEEL_CHAR_K;
5754       break;
5755
5756     case 0x1620:        // (blue steel)
5757       element = EL_STEEL_CHAR_L;
5758       break;
5759
5760     case 0x1621:        // (blue steel)
5761       element = EL_STEEL_CHAR_M;
5762       break;
5763
5764     case 0x1622:        // (blue steel)
5765       element = EL_STEEL_CHAR_N;
5766       break;
5767
5768     case 0x1623:        // (blue steel)
5769       element = EL_STEEL_CHAR_O;
5770       break;
5771
5772     case 0x1624:        // (blue steel)
5773       element = EL_STEEL_CHAR_P;
5774       break;
5775
5776     case 0x1625:        // (blue steel)
5777       element = EL_STEEL_CHAR_Q;
5778       break;
5779
5780     case 0x1626:        // (blue steel)
5781       element = EL_STEEL_CHAR_R;
5782       break;
5783
5784     case 0x1627:        // (blue steel)
5785       element = EL_STEEL_CHAR_S;
5786       break;
5787
5788     case 0x1628:        // (blue steel)
5789       element = EL_STEEL_CHAR_T;
5790       break;
5791
5792     case 0x1629:        // (blue steel)
5793       element = EL_STEEL_CHAR_U;
5794       break;
5795
5796     case 0x162a:        // (blue steel)
5797       element = EL_STEEL_CHAR_V;
5798       break;
5799
5800     case 0x162b:        // (blue steel)
5801       element = EL_STEEL_CHAR_W;
5802       break;
5803
5804     case 0x162c:        // (blue steel)
5805       element = EL_STEEL_CHAR_X;
5806       break;
5807
5808     case 0x162d:        // (blue steel)
5809       element = EL_STEEL_CHAR_Y;
5810       break;
5811
5812     case 0x162e:        // (blue steel)
5813       element = EL_STEEL_CHAR_Z;
5814       break;
5815
5816     case 0x162f:        // (blue steel)
5817       element = EL_STEEL_CHAR_AUMLAUT;
5818       break;
5819
5820     case 0x1630:        // (blue steel)
5821       element = EL_STEEL_CHAR_OUMLAUT;
5822       break;
5823
5824     case 0x1631:        // (blue steel)
5825       element = EL_STEEL_CHAR_UUMLAUT;
5826       break;
5827
5828     case 0x1632:        // (blue steel)
5829       element = EL_STEEL_CHAR_0;
5830       break;
5831
5832     case 0x1633:        // (blue steel)
5833       element = EL_STEEL_CHAR_1;
5834       break;
5835
5836     case 0x1634:        // (blue steel)
5837       element = EL_STEEL_CHAR_2;
5838       break;
5839
5840     case 0x1635:        // (blue steel)
5841       element = EL_STEEL_CHAR_3;
5842       break;
5843
5844     case 0x1636:        // (blue steel)
5845       element = EL_STEEL_CHAR_4;
5846       break;
5847
5848     case 0x1637:        // (blue steel)
5849       element = EL_STEEL_CHAR_5;
5850       break;
5851
5852     case 0x1638:        // (blue steel)
5853       element = EL_STEEL_CHAR_6;
5854       break;
5855
5856     case 0x1639:        // (blue steel)
5857       element = EL_STEEL_CHAR_7;
5858       break;
5859
5860     case 0x163a:        // (blue steel)
5861       element = EL_STEEL_CHAR_8;
5862       break;
5863
5864     case 0x163b:        // (blue steel)
5865       element = EL_STEEL_CHAR_9;
5866       break;
5867
5868     case 0x163c:        // (blue steel)
5869       element = EL_STEEL_CHAR_PERIOD;
5870       break;
5871
5872     case 0x163d:        // (blue steel)
5873       element = EL_STEEL_CHAR_EXCLAM;
5874       break;
5875
5876     case 0x163e:        // (blue steel)
5877       element = EL_STEEL_CHAR_COLON;
5878       break;
5879
5880     case 0x163f:        // (blue steel)
5881       element = EL_STEEL_CHAR_LESS;
5882       break;
5883
5884     case 0x1640:        // (blue steel)
5885       element = EL_STEEL_CHAR_GREATER;
5886       break;
5887
5888     case 0x1641:        // (blue steel)
5889       element = EL_STEEL_CHAR_QUESTION;
5890       break;
5891
5892     case 0x1642:        // (blue steel)
5893       element = EL_STEEL_CHAR_COPYRIGHT;
5894       break;
5895
5896     case 0x1643:        // (blue steel)
5897       element = EL_STEEL_CHAR_UP;
5898       break;
5899
5900     case 0x1644:        // (blue steel)
5901       element = EL_STEEL_CHAR_DOWN;
5902       break;
5903
5904     case 0x1645:        // (blue steel)
5905       element = EL_STEEL_CHAR_BUTTON;
5906       break;
5907
5908     case 0x1646:        // (blue steel)
5909       element = EL_STEEL_CHAR_PLUS;
5910       break;
5911
5912     case 0x1647:        // (blue steel)
5913       element = EL_STEEL_CHAR_MINUS;
5914       break;
5915
5916     case 0x1648:        // (blue steel)
5917       element = EL_STEEL_CHAR_APOSTROPHE;
5918       break;
5919
5920     case 0x1649:        // (blue steel)
5921       element = EL_STEEL_CHAR_PARENLEFT;
5922       break;
5923
5924     case 0x164a:        // (blue steel)
5925       element = EL_STEEL_CHAR_PARENRIGHT;
5926       break;
5927
5928     case 0x164b:        // (green steel)
5929       element = EL_STEEL_CHAR_A;
5930       break;
5931
5932     case 0x164c:        // (green steel)
5933       element = EL_STEEL_CHAR_B;
5934       break;
5935
5936     case 0x164d:        // (green steel)
5937       element = EL_STEEL_CHAR_C;
5938       break;
5939
5940     case 0x164e:        // (green steel)
5941       element = EL_STEEL_CHAR_D;
5942       break;
5943
5944     case 0x164f:        // (green steel)
5945       element = EL_STEEL_CHAR_E;
5946       break;
5947
5948     case 0x1650:        // (green steel)
5949       element = EL_STEEL_CHAR_F;
5950       break;
5951
5952     case 0x1651:        // (green steel)
5953       element = EL_STEEL_CHAR_G;
5954       break;
5955
5956     case 0x1652:        // (green steel)
5957       element = EL_STEEL_CHAR_H;
5958       break;
5959
5960     case 0x1653:        // (green steel)
5961       element = EL_STEEL_CHAR_I;
5962       break;
5963
5964     case 0x1654:        // (green steel)
5965       element = EL_STEEL_CHAR_J;
5966       break;
5967
5968     case 0x1655:        // (green steel)
5969       element = EL_STEEL_CHAR_K;
5970       break;
5971
5972     case 0x1656:        // (green steel)
5973       element = EL_STEEL_CHAR_L;
5974       break;
5975
5976     case 0x1657:        // (green steel)
5977       element = EL_STEEL_CHAR_M;
5978       break;
5979
5980     case 0x1658:        // (green steel)
5981       element = EL_STEEL_CHAR_N;
5982       break;
5983
5984     case 0x1659:        // (green steel)
5985       element = EL_STEEL_CHAR_O;
5986       break;
5987
5988     case 0x165a:        // (green steel)
5989       element = EL_STEEL_CHAR_P;
5990       break;
5991
5992     case 0x165b:        // (green steel)
5993       element = EL_STEEL_CHAR_Q;
5994       break;
5995
5996     case 0x165c:        // (green steel)
5997       element = EL_STEEL_CHAR_R;
5998       break;
5999
6000     case 0x165d:        // (green steel)
6001       element = EL_STEEL_CHAR_S;
6002       break;
6003
6004     case 0x165e:        // (green steel)
6005       element = EL_STEEL_CHAR_T;
6006       break;
6007
6008     case 0x165f:        // (green steel)
6009       element = EL_STEEL_CHAR_U;
6010       break;
6011
6012     case 0x1660:        // (green steel)
6013       element = EL_STEEL_CHAR_V;
6014       break;
6015
6016     case 0x1661:        // (green steel)
6017       element = EL_STEEL_CHAR_W;
6018       break;
6019
6020     case 0x1662:        // (green steel)
6021       element = EL_STEEL_CHAR_X;
6022       break;
6023
6024     case 0x1663:        // (green steel)
6025       element = EL_STEEL_CHAR_Y;
6026       break;
6027
6028     case 0x1664:        // (green steel)
6029       element = EL_STEEL_CHAR_Z;
6030       break;
6031
6032     case 0x1665:        // (green steel)
6033       element = EL_STEEL_CHAR_AUMLAUT;
6034       break;
6035
6036     case 0x1666:        // (green steel)
6037       element = EL_STEEL_CHAR_OUMLAUT;
6038       break;
6039
6040     case 0x1667:        // (green steel)
6041       element = EL_STEEL_CHAR_UUMLAUT;
6042       break;
6043
6044     case 0x1668:        // (green steel)
6045       element = EL_STEEL_CHAR_0;
6046       break;
6047
6048     case 0x1669:        // (green steel)
6049       element = EL_STEEL_CHAR_1;
6050       break;
6051
6052     case 0x166a:        // (green steel)
6053       element = EL_STEEL_CHAR_2;
6054       break;
6055
6056     case 0x166b:        // (green steel)
6057       element = EL_STEEL_CHAR_3;
6058       break;
6059
6060     case 0x166c:        // (green steel)
6061       element = EL_STEEL_CHAR_4;
6062       break;
6063
6064     case 0x166d:        // (green steel)
6065       element = EL_STEEL_CHAR_5;
6066       break;
6067
6068     case 0x166e:        // (green steel)
6069       element = EL_STEEL_CHAR_6;
6070       break;
6071
6072     case 0x166f:        // (green steel)
6073       element = EL_STEEL_CHAR_7;
6074       break;
6075
6076     case 0x1670:        // (green steel)
6077       element = EL_STEEL_CHAR_8;
6078       break;
6079
6080     case 0x1671:        // (green steel)
6081       element = EL_STEEL_CHAR_9;
6082       break;
6083
6084     case 0x1672:        // (green steel)
6085       element = EL_STEEL_CHAR_PERIOD;
6086       break;
6087
6088     case 0x1673:        // (green steel)
6089       element = EL_STEEL_CHAR_EXCLAM;
6090       break;
6091
6092     case 0x1674:        // (green steel)
6093       element = EL_STEEL_CHAR_COLON;
6094       break;
6095
6096     case 0x1675:        // (green steel)
6097       element = EL_STEEL_CHAR_LESS;
6098       break;
6099
6100     case 0x1676:        // (green steel)
6101       element = EL_STEEL_CHAR_GREATER;
6102       break;
6103
6104     case 0x1677:        // (green steel)
6105       element = EL_STEEL_CHAR_QUESTION;
6106       break;
6107
6108     case 0x1678:        // (green steel)
6109       element = EL_STEEL_CHAR_COPYRIGHT;
6110       break;
6111
6112     case 0x1679:        // (green steel)
6113       element = EL_STEEL_CHAR_UP;
6114       break;
6115
6116     case 0x167a:        // (green steel)
6117       element = EL_STEEL_CHAR_DOWN;
6118       break;
6119
6120     case 0x167b:        // (green steel)
6121       element = EL_STEEL_CHAR_BUTTON;
6122       break;
6123
6124     case 0x167c:        // (green steel)
6125       element = EL_STEEL_CHAR_PLUS;
6126       break;
6127
6128     case 0x167d:        // (green steel)
6129       element = EL_STEEL_CHAR_MINUS;
6130       break;
6131
6132     case 0x167e:        // (green steel)
6133       element = EL_STEEL_CHAR_APOSTROPHE;
6134       break;
6135
6136     case 0x167f:        // (green steel)
6137       element = EL_STEEL_CHAR_PARENLEFT;
6138       break;
6139
6140     case 0x1680:        // (green steel)
6141       element = EL_STEEL_CHAR_PARENRIGHT;
6142       break;
6143
6144     case 0x1681:        // gate (red)
6145       element = EL_EM_GATE_1;
6146       break;
6147
6148     case 0x1682:        // secret gate (red)
6149       element = EL_EM_GATE_1_GRAY;
6150       break;
6151
6152     case 0x1683:        // gate (yellow)
6153       element = EL_EM_GATE_2;
6154       break;
6155
6156     case 0x1684:        // secret gate (yellow)
6157       element = EL_EM_GATE_2_GRAY;
6158       break;
6159
6160     case 0x1685:        // gate (blue)
6161       element = EL_EM_GATE_4;
6162       break;
6163
6164     case 0x1686:        // secret gate (blue)
6165       element = EL_EM_GATE_4_GRAY;
6166       break;
6167
6168     case 0x1687:        // gate (green)
6169       element = EL_EM_GATE_3;
6170       break;
6171
6172     case 0x1688:        // secret gate (green)
6173       element = EL_EM_GATE_3_GRAY;
6174       break;
6175
6176     case 0x1689:        // gate (white)
6177       element = EL_DC_GATE_WHITE;
6178       break;
6179
6180     case 0x168a:        // secret gate (white)
6181       element = EL_DC_GATE_WHITE_GRAY;
6182       break;
6183
6184     case 0x168b:        // secret gate (no key)
6185       element = EL_DC_GATE_FAKE_GRAY;
6186       break;
6187
6188     case 0x168c:
6189       element = EL_ROBOT_WHEEL;
6190       break;
6191
6192     case 0x168d:
6193       element = EL_DC_TIMEGATE_SWITCH;
6194       break;
6195
6196     case 0x168e:
6197       element = EL_ACID_POOL_BOTTOM;
6198       break;
6199
6200     case 0x168f:
6201       element = EL_ACID_POOL_TOPLEFT;
6202       break;
6203
6204     case 0x1690:
6205       element = EL_ACID_POOL_TOPRIGHT;
6206       break;
6207
6208     case 0x1691:
6209       element = EL_ACID_POOL_BOTTOMLEFT;
6210       break;
6211
6212     case 0x1692:
6213       element = EL_ACID_POOL_BOTTOMRIGHT;
6214       break;
6215
6216     case 0x1693:
6217       element = EL_STEELWALL;
6218       break;
6219
6220     case 0x1694:
6221       element = EL_STEELWALL_SLIPPERY;
6222       break;
6223
6224     case 0x1695:        // steel wall (not round)
6225       element = EL_STEELWALL;
6226       break;
6227
6228     case 0x1696:        // steel wall (left)
6229       element = EL_DC_STEELWALL_1_LEFT;
6230       break;
6231
6232     case 0x1697:        // steel wall (bottom)
6233       element = EL_DC_STEELWALL_1_BOTTOM;
6234       break;
6235
6236     case 0x1698:        // steel wall (right)
6237       element = EL_DC_STEELWALL_1_RIGHT;
6238       break;
6239
6240     case 0x1699:        // steel wall (top)
6241       element = EL_DC_STEELWALL_1_TOP;
6242       break;
6243
6244     case 0x169a:        // steel wall (left/bottom)
6245       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6246       break;
6247
6248     case 0x169b:        // steel wall (right/bottom)
6249       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6250       break;
6251
6252     case 0x169c:        // steel wall (right/top)
6253       element = EL_DC_STEELWALL_1_TOPRIGHT;
6254       break;
6255
6256     case 0x169d:        // steel wall (left/top)
6257       element = EL_DC_STEELWALL_1_TOPLEFT;
6258       break;
6259
6260     case 0x169e:        // steel wall (right/bottom small)
6261       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6262       break;
6263
6264     case 0x169f:        // steel wall (left/bottom small)
6265       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6266       break;
6267
6268     case 0x16a0:        // steel wall (right/top small)
6269       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6270       break;
6271
6272     case 0x16a1:        // steel wall (left/top small)
6273       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6274       break;
6275
6276     case 0x16a2:        // steel wall (left/right)
6277       element = EL_DC_STEELWALL_1_VERTICAL;
6278       break;
6279
6280     case 0x16a3:        // steel wall (top/bottom)
6281       element = EL_DC_STEELWALL_1_HORIZONTAL;
6282       break;
6283
6284     case 0x16a4:        // steel wall 2 (left end)
6285       element = EL_DC_STEELWALL_2_LEFT;
6286       break;
6287
6288     case 0x16a5:        // steel wall 2 (right end)
6289       element = EL_DC_STEELWALL_2_RIGHT;
6290       break;
6291
6292     case 0x16a6:        // steel wall 2 (top end)
6293       element = EL_DC_STEELWALL_2_TOP;
6294       break;
6295
6296     case 0x16a7:        // steel wall 2 (bottom end)
6297       element = EL_DC_STEELWALL_2_BOTTOM;
6298       break;
6299
6300     case 0x16a8:        // steel wall 2 (left/right)
6301       element = EL_DC_STEELWALL_2_HORIZONTAL;
6302       break;
6303
6304     case 0x16a9:        // steel wall 2 (up/down)
6305       element = EL_DC_STEELWALL_2_VERTICAL;
6306       break;
6307
6308     case 0x16aa:        // steel wall 2 (mid)
6309       element = EL_DC_STEELWALL_2_MIDDLE;
6310       break;
6311
6312     case 0x16ab:
6313       element = EL_SIGN_EXCLAMATION;
6314       break;
6315
6316     case 0x16ac:
6317       element = EL_SIGN_RADIOACTIVITY;
6318       break;
6319
6320     case 0x16ad:
6321       element = EL_SIGN_STOP;
6322       break;
6323
6324     case 0x16ae:
6325       element = EL_SIGN_WHEELCHAIR;
6326       break;
6327
6328     case 0x16af:
6329       element = EL_SIGN_PARKING;
6330       break;
6331
6332     case 0x16b0:
6333       element = EL_SIGN_NO_ENTRY;
6334       break;
6335
6336     case 0x16b1:
6337       element = EL_SIGN_HEART;
6338       break;
6339
6340     case 0x16b2:
6341       element = EL_SIGN_GIVE_WAY;
6342       break;
6343
6344     case 0x16b3:
6345       element = EL_SIGN_ENTRY_FORBIDDEN;
6346       break;
6347
6348     case 0x16b4:
6349       element = EL_SIGN_EMERGENCY_EXIT;
6350       break;
6351
6352     case 0x16b5:
6353       element = EL_SIGN_YIN_YANG;
6354       break;
6355
6356     case 0x16b6:
6357       element = EL_WALL_EMERALD;
6358       break;
6359
6360     case 0x16b7:
6361       element = EL_WALL_DIAMOND;
6362       break;
6363
6364     case 0x16b8:
6365       element = EL_WALL_PEARL;
6366       break;
6367
6368     case 0x16b9:
6369       element = EL_WALL_CRYSTAL;
6370       break;
6371
6372     case 0x16ba:
6373       element = EL_INVISIBLE_WALL;
6374       break;
6375
6376     case 0x16bb:
6377       element = EL_INVISIBLE_STEELWALL;
6378       break;
6379
6380       // 0x16bc - 0x16cb:
6381       // EL_INVISIBLE_SAND
6382
6383     case 0x16cc:
6384       element = EL_LIGHT_SWITCH;
6385       break;
6386
6387     case 0x16cd:
6388       element = EL_ENVELOPE_1;
6389       break;
6390
6391     default:
6392       if (element >= 0x0117 && element <= 0x036e)       // (?)
6393         element = EL_DIAMOND;
6394       else if (element >= 0x042d && element <= 0x0684)  // (?)
6395         element = EL_EMERALD;
6396       else if (element >= 0x157c && element <= 0x158b)
6397         element = EL_SAND;
6398       else if (element >= 0x1590 && element <= 0x159f)
6399         element = EL_DC_LANDMINE;
6400       else if (element >= 0x16bc && element <= 0x16cb)
6401         element = EL_INVISIBLE_SAND;
6402       else
6403       {
6404         Warn("unknown Diamond Caves element 0x%04x", element);
6405
6406         element = EL_UNKNOWN;
6407       }
6408       break;
6409   }
6410
6411   return getMappedElement(element);
6412 }
6413
6414 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6415 {
6416   byte header[DC_LEVEL_HEADER_SIZE];
6417   int envelope_size;
6418   int envelope_header_pos = 62;
6419   int envelope_content_pos = 94;
6420   int level_name_pos = 251;
6421   int level_author_pos = 292;
6422   int envelope_header_len;
6423   int envelope_content_len;
6424   int level_name_len;
6425   int level_author_len;
6426   int fieldx, fieldy;
6427   int num_yamyam_contents;
6428   int i, x, y;
6429
6430   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6431
6432   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6433   {
6434     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6435
6436     header[i * 2 + 0] = header_word >> 8;
6437     header[i * 2 + 1] = header_word & 0xff;
6438   }
6439
6440   // read some values from level header to check level decoding integrity
6441   fieldx = header[6] | (header[7] << 8);
6442   fieldy = header[8] | (header[9] << 8);
6443   num_yamyam_contents = header[60] | (header[61] << 8);
6444
6445   // do some simple sanity checks to ensure that level was correctly decoded
6446   if (fieldx < 1 || fieldx > 256 ||
6447       fieldy < 1 || fieldy > 256 ||
6448       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6449   {
6450     level->no_valid_file = TRUE;
6451
6452     Warn("cannot decode level from stream -- using empty level");
6453
6454     return;
6455   }
6456
6457   // maximum envelope header size is 31 bytes
6458   envelope_header_len   = header[envelope_header_pos];
6459   // maximum envelope content size is 110 (156?) bytes
6460   envelope_content_len  = header[envelope_content_pos];
6461
6462   // maximum level title size is 40 bytes
6463   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6464   // maximum level author size is 30 (51?) bytes
6465   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6466
6467   envelope_size = 0;
6468
6469   for (i = 0; i < envelope_header_len; i++)
6470     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6471       level->envelope[0].text[envelope_size++] =
6472         header[envelope_header_pos + 1 + i];
6473
6474   if (envelope_header_len > 0 && envelope_content_len > 0)
6475   {
6476     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6477       level->envelope[0].text[envelope_size++] = '\n';
6478     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6479       level->envelope[0].text[envelope_size++] = '\n';
6480   }
6481
6482   for (i = 0; i < envelope_content_len; i++)
6483     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6484       level->envelope[0].text[envelope_size++] =
6485         header[envelope_content_pos + 1 + i];
6486
6487   level->envelope[0].text[envelope_size] = '\0';
6488
6489   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6490   level->envelope[0].ysize = 10;
6491   level->envelope[0].autowrap = TRUE;
6492   level->envelope[0].centered = TRUE;
6493
6494   for (i = 0; i < level_name_len; i++)
6495     level->name[i] = header[level_name_pos + 1 + i];
6496   level->name[level_name_len] = '\0';
6497
6498   for (i = 0; i < level_author_len; i++)
6499     level->author[i] = header[level_author_pos + 1 + i];
6500   level->author[level_author_len] = '\0';
6501
6502   num_yamyam_contents = header[60] | (header[61] << 8);
6503   level->num_yamyam_contents =
6504     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6505
6506   for (i = 0; i < num_yamyam_contents; i++)
6507   {
6508     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6509     {
6510       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6511       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6512
6513       if (i < MAX_ELEMENT_CONTENTS)
6514         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6515     }
6516   }
6517
6518   fieldx = header[6] | (header[7] << 8);
6519   fieldy = header[8] | (header[9] << 8);
6520   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6521   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6522
6523   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6524   {
6525     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6526     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6527
6528     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6529       level->field[x][y] = getMappedElement_DC(element_dc);
6530   }
6531
6532   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6533   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6534   level->field[x][y] = EL_PLAYER_1;
6535
6536   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6537   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6538   level->field[x][y] = EL_PLAYER_2;
6539
6540   level->gems_needed            = header[18] | (header[19] << 8);
6541
6542   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6543   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6544   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6545   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6546   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6547   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6548   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6549   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6550   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6551   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6552   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6553   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6554
6555   level->time                   = header[44] | (header[45] << 8);
6556
6557   level->amoeba_speed           = header[46] | (header[47] << 8);
6558   level->time_light             = header[48] | (header[49] << 8);
6559   level->time_timegate          = header[50] | (header[51] << 8);
6560   level->time_wheel             = header[52] | (header[53] << 8);
6561   level->time_magic_wall        = header[54] | (header[55] << 8);
6562   level->extra_time             = header[56] | (header[57] << 8);
6563   level->shield_normal_time     = header[58] | (header[59] << 8);
6564
6565   // shield and extra time elements do not have a score
6566   level->score[SC_SHIELD]       = 0;
6567   level->extra_time_score       = 0;
6568
6569   // set time for normal and deadly shields to the same value
6570   level->shield_deadly_time     = level->shield_normal_time;
6571
6572   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6573   // can slip down from flat walls, like normal walls and steel walls
6574   level->em_slippery_gems = TRUE;
6575
6576   // time score is counted for each 10 seconds left in Diamond Caves levels
6577   level->time_score_base = 10;
6578 }
6579
6580 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6581                                      struct LevelFileInfo *level_file_info,
6582                                      boolean level_info_only)
6583 {
6584   char *filename = level_file_info->filename;
6585   File *file;
6586   int num_magic_bytes = 8;
6587   char magic_bytes[num_magic_bytes + 1];
6588   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6589
6590   if (!(file = openFile(filename, MODE_READ)))
6591   {
6592     level->no_valid_file = TRUE;
6593
6594     if (!level_info_only)
6595       Warn("cannot read level '%s' -- using empty level", filename);
6596
6597     return;
6598   }
6599
6600   // fseek(file, 0x0000, SEEK_SET);
6601
6602   if (level_file_info->packed)
6603   {
6604     // read "magic bytes" from start of file
6605     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6606       magic_bytes[0] = '\0';
6607
6608     // check "magic bytes" for correct file format
6609     if (!strPrefix(magic_bytes, "DC2"))
6610     {
6611       level->no_valid_file = TRUE;
6612
6613       Warn("unknown DC level file '%s' -- using empty level", filename);
6614
6615       return;
6616     }
6617
6618     if (strPrefix(magic_bytes, "DC2Win95") ||
6619         strPrefix(magic_bytes, "DC2Win98"))
6620     {
6621       int position_first_level = 0x00fa;
6622       int extra_bytes = 4;
6623       int skip_bytes;
6624
6625       // advance file stream to first level inside the level package
6626       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6627
6628       // each block of level data is followed by block of non-level data
6629       num_levels_to_skip *= 2;
6630
6631       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6632       while (num_levels_to_skip >= 0)
6633       {
6634         // advance file stream to next level inside the level package
6635         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6636         {
6637           level->no_valid_file = TRUE;
6638
6639           Warn("cannot fseek in file '%s' -- using empty level", filename);
6640
6641           return;
6642         }
6643
6644         // skip apparently unused extra bytes following each level
6645         ReadUnusedBytesFromFile(file, extra_bytes);
6646
6647         // read size of next level in level package
6648         skip_bytes = getFile32BitLE(file);
6649
6650         num_levels_to_skip--;
6651       }
6652     }
6653     else
6654     {
6655       level->no_valid_file = TRUE;
6656
6657       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6658
6659       return;
6660     }
6661   }
6662
6663   LoadLevelFromFileStream_DC(file, level);
6664
6665   closeFile(file);
6666 }
6667
6668
6669 // ----------------------------------------------------------------------------
6670 // functions for loading SB level
6671 // ----------------------------------------------------------------------------
6672
6673 int getMappedElement_SB(int element_ascii, boolean use_ces)
6674 {
6675   static struct
6676   {
6677     int ascii;
6678     int sb;
6679     int ce;
6680   }
6681   sb_element_mapping[] =
6682   {
6683     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6684     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6685     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6686     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6687     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6688     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6689     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6690     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6691
6692     { 0,   -1,                      -1          },
6693   };
6694
6695   int i;
6696
6697   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6698     if (element_ascii == sb_element_mapping[i].ascii)
6699       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6700
6701   return EL_UNDEFINED;
6702 }
6703
6704 static void SetLevelSettings_SB(struct LevelInfo *level)
6705 {
6706   // time settings
6707   level->time = 0;
6708   level->use_step_counter = TRUE;
6709
6710   // score settings
6711   level->score[SC_TIME_BONUS] = 0;
6712   level->time_score_base = 1;
6713   level->rate_time_over_score = TRUE;
6714
6715   // game settings
6716   level->auto_exit_sokoban = TRUE;
6717 }
6718
6719 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6720                                      struct LevelFileInfo *level_file_info,
6721                                      boolean level_info_only)
6722 {
6723   char *filename = level_file_info->filename;
6724   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6725   char last_comment[MAX_LINE_LEN];
6726   char level_name[MAX_LINE_LEN];
6727   char *line_ptr;
6728   File *file;
6729   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6730   boolean read_continued_line = FALSE;
6731   boolean reading_playfield = FALSE;
6732   boolean got_valid_playfield_line = FALSE;
6733   boolean invalid_playfield_char = FALSE;
6734   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6735   int file_level_nr = 0;
6736   int x = 0, y = 0;             // initialized to make compilers happy
6737
6738   last_comment[0] = '\0';
6739   level_name[0] = '\0';
6740
6741   if (!(file = openFile(filename, MODE_READ)))
6742   {
6743     level->no_valid_file = TRUE;
6744
6745     if (!level_info_only)
6746       Warn("cannot read level '%s' -- using empty level", filename);
6747
6748     return;
6749   }
6750
6751   while (!checkEndOfFile(file))
6752   {
6753     // level successfully read, but next level may follow here
6754     if (!got_valid_playfield_line && reading_playfield)
6755     {
6756       // read playfield from single level file -- skip remaining file
6757       if (!level_file_info->packed)
6758         break;
6759
6760       if (file_level_nr >= num_levels_to_skip)
6761         break;
6762
6763       file_level_nr++;
6764
6765       last_comment[0] = '\0';
6766       level_name[0] = '\0';
6767
6768       reading_playfield = FALSE;
6769     }
6770
6771     got_valid_playfield_line = FALSE;
6772
6773     // read next line of input file
6774     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6775       break;
6776
6777     // cut trailing line break (this can be newline and/or carriage return)
6778     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6779       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6780         *line_ptr = '\0';
6781
6782     // copy raw input line for later use (mainly debugging output)
6783     strcpy(line_raw, line);
6784
6785     if (read_continued_line)
6786     {
6787       // append new line to existing line, if there is enough space
6788       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6789         strcat(previous_line, line_ptr);
6790
6791       strcpy(line, previous_line);      // copy storage buffer to line
6792
6793       read_continued_line = FALSE;
6794     }
6795
6796     // if the last character is '\', continue at next line
6797     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6798     {
6799       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6800       strcpy(previous_line, line);      // copy line to storage buffer
6801
6802       read_continued_line = TRUE;
6803
6804       continue;
6805     }
6806
6807     // skip empty lines
6808     if (line[0] == '\0')
6809       continue;
6810
6811     // extract comment text from comment line
6812     if (line[0] == ';')
6813     {
6814       for (line_ptr = line; *line_ptr; line_ptr++)
6815         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6816           break;
6817
6818       strcpy(last_comment, line_ptr);
6819
6820       continue;
6821     }
6822
6823     // extract level title text from line containing level title
6824     if (line[0] == '\'')
6825     {
6826       strcpy(level_name, &line[1]);
6827
6828       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6829         level_name[strlen(level_name) - 1] = '\0';
6830
6831       continue;
6832     }
6833
6834     // skip lines containing only spaces (or empty lines)
6835     for (line_ptr = line; *line_ptr; line_ptr++)
6836       if (*line_ptr != ' ')
6837         break;
6838     if (*line_ptr == '\0')
6839       continue;
6840
6841     // at this point, we have found a line containing part of a playfield
6842
6843     got_valid_playfield_line = TRUE;
6844
6845     if (!reading_playfield)
6846     {
6847       reading_playfield = TRUE;
6848       invalid_playfield_char = FALSE;
6849
6850       for (x = 0; x < MAX_LEV_FIELDX; x++)
6851         for (y = 0; y < MAX_LEV_FIELDY; y++)
6852           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6853
6854       level->fieldx = 0;
6855       level->fieldy = 0;
6856
6857       // start with topmost tile row
6858       y = 0;
6859     }
6860
6861     // skip playfield line if larger row than allowed
6862     if (y >= MAX_LEV_FIELDY)
6863       continue;
6864
6865     // start with leftmost tile column
6866     x = 0;
6867
6868     // read playfield elements from line
6869     for (line_ptr = line; *line_ptr; line_ptr++)
6870     {
6871       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6872
6873       // stop parsing playfield line if larger column than allowed
6874       if (x >= MAX_LEV_FIELDX)
6875         break;
6876
6877       if (mapped_sb_element == EL_UNDEFINED)
6878       {
6879         invalid_playfield_char = TRUE;
6880
6881         break;
6882       }
6883
6884       level->field[x][y] = mapped_sb_element;
6885
6886       // continue with next tile column
6887       x++;
6888
6889       level->fieldx = MAX(x, level->fieldx);
6890     }
6891
6892     if (invalid_playfield_char)
6893     {
6894       // if first playfield line, treat invalid lines as comment lines
6895       if (y == 0)
6896         reading_playfield = FALSE;
6897
6898       continue;
6899     }
6900
6901     // continue with next tile row
6902     y++;
6903   }
6904
6905   closeFile(file);
6906
6907   level->fieldy = y;
6908
6909   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6910   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6911
6912   if (!reading_playfield)
6913   {
6914     level->no_valid_file = TRUE;
6915
6916     Warn("cannot read level '%s' -- using empty level", filename);
6917
6918     return;
6919   }
6920
6921   if (*level_name != '\0')
6922   {
6923     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6924     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6925   }
6926   else if (*last_comment != '\0')
6927   {
6928     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6929     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6930   }
6931   else
6932   {
6933     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6934   }
6935
6936   // set all empty fields beyond the border walls to invisible steel wall
6937   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6938   {
6939     if ((x == 0 || x == level->fieldx - 1 ||
6940          y == 0 || y == level->fieldy - 1) &&
6941         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6942       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6943                      level->field, level->fieldx, level->fieldy);
6944   }
6945
6946   // set special level settings for Sokoban levels
6947   SetLevelSettings_SB(level);
6948
6949   if (load_xsb_to_ces)
6950   {
6951     // special global settings can now be set in level template
6952     level->use_custom_template = TRUE;
6953   }
6954 }
6955
6956
6957 // -------------------------------------------------------------------------
6958 // functions for handling native levels
6959 // -------------------------------------------------------------------------
6960
6961 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6962                                      struct LevelFileInfo *level_file_info,
6963                                      boolean level_info_only)
6964 {
6965   int pos = 0;
6966
6967   // determine position of requested level inside level package
6968   if (level_file_info->packed)
6969     pos = level_file_info->nr - leveldir_current->first_level;
6970
6971   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6972     level->no_valid_file = TRUE;
6973 }
6974
6975 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6976                                      struct LevelFileInfo *level_file_info,
6977                                      boolean level_info_only)
6978 {
6979   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6980     level->no_valid_file = TRUE;
6981 }
6982
6983 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6984                                      struct LevelFileInfo *level_file_info,
6985                                      boolean level_info_only)
6986 {
6987   int pos = 0;
6988
6989   // determine position of requested level inside level package
6990   if (level_file_info->packed)
6991     pos = level_file_info->nr - leveldir_current->first_level;
6992
6993   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6994     level->no_valid_file = TRUE;
6995 }
6996
6997 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6998                                      struct LevelFileInfo *level_file_info,
6999                                      boolean level_info_only)
7000 {
7001   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7002     level->no_valid_file = TRUE;
7003 }
7004
7005 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7006 {
7007   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7008     CopyNativeLevel_RND_to_BD(level);
7009   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7010     CopyNativeLevel_RND_to_EM(level);
7011   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7012     CopyNativeLevel_RND_to_SP(level);
7013   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7014     CopyNativeLevel_RND_to_MM(level);
7015 }
7016
7017 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7018 {
7019   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7020     CopyNativeLevel_BD_to_RND(level);
7021   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7022     CopyNativeLevel_EM_to_RND(level);
7023   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7024     CopyNativeLevel_SP_to_RND(level);
7025   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7026     CopyNativeLevel_MM_to_RND(level);
7027 }
7028
7029 void SaveNativeLevel(struct LevelInfo *level)
7030 {
7031   // saving native level files only supported for some game engines
7032   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7033       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7034     return;
7035
7036   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7037                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7038   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7039   char *filename = getLevelFilenameFromBasename(basename);
7040
7041   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7042     return;
7043
7044   boolean success = FALSE;
7045
7046   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7047   {
7048     CopyNativeLevel_RND_to_BD(level);
7049     // CopyNativeTape_RND_to_BD(level);
7050
7051     success = SaveNativeLevel_BD(filename);
7052   }
7053   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7054   {
7055     CopyNativeLevel_RND_to_SP(level);
7056     CopyNativeTape_RND_to_SP(level);
7057
7058     success = SaveNativeLevel_SP(filename);
7059   }
7060
7061   if (success)
7062     Request("Native level file saved!", REQ_CONFIRM);
7063   else
7064     Request("Failed to save native level file!", REQ_CONFIRM);
7065 }
7066
7067
7068 // ----------------------------------------------------------------------------
7069 // functions for loading generic level
7070 // ----------------------------------------------------------------------------
7071
7072 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7073                                   struct LevelFileInfo *level_file_info,
7074                                   boolean level_info_only)
7075 {
7076   // always start with reliable default values
7077   setLevelInfoToDefaults(level, level_info_only, TRUE);
7078
7079   switch (level_file_info->type)
7080   {
7081     case LEVEL_FILE_TYPE_RND:
7082       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7083       break;
7084
7085     case LEVEL_FILE_TYPE_BD:
7086       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7087       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7088       break;
7089
7090     case LEVEL_FILE_TYPE_EM:
7091       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7092       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7093       break;
7094
7095     case LEVEL_FILE_TYPE_SP:
7096       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7097       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7098       break;
7099
7100     case LEVEL_FILE_TYPE_MM:
7101       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7102       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7103       break;
7104
7105     case LEVEL_FILE_TYPE_DC:
7106       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7107       break;
7108
7109     case LEVEL_FILE_TYPE_SB:
7110       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7111       break;
7112
7113     default:
7114       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7115       break;
7116   }
7117
7118   // if level file is invalid, restore level structure to default values
7119   if (level->no_valid_file)
7120     setLevelInfoToDefaults(level, level_info_only, FALSE);
7121
7122   if (check_special_flags("use_native_bd_game_engine"))
7123     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7124
7125   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7126     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7127
7128   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7129     CopyNativeLevel_Native_to_RND(level);
7130 }
7131
7132 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7133 {
7134   static struct LevelFileInfo level_file_info;
7135
7136   // always start with reliable default values
7137   setFileInfoToDefaults(&level_file_info);
7138
7139   level_file_info.nr = 0;                       // unknown level number
7140   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7141
7142   setString(&level_file_info.filename, filename);
7143
7144   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7145 }
7146
7147 static void LoadLevel_InitVersion(struct LevelInfo *level)
7148 {
7149   int i, j;
7150
7151   if (leveldir_current == NULL)         // only when dumping level
7152     return;
7153
7154   // all engine modifications also valid for levels which use latest engine
7155   if (level->game_version < VERSION_IDENT(3,2,0,5))
7156   {
7157     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7158     level->time_score_base = 10;
7159   }
7160
7161   if (leveldir_current->latest_engine)
7162   {
7163     // ---------- use latest game engine --------------------------------------
7164
7165     /* For all levels which are forced to use the latest game engine version
7166        (normally all but user contributed, private and undefined levels), set
7167        the game engine version to the actual version; this allows for actual
7168        corrections in the game engine to take effect for existing, converted
7169        levels (from "classic" or other existing games) to make the emulation
7170        of the corresponding game more accurate, while (hopefully) not breaking
7171        existing levels created from other players. */
7172
7173     level->game_version = GAME_VERSION_ACTUAL;
7174
7175     /* Set special EM style gems behaviour: EM style gems slip down from
7176        normal, steel and growing wall. As this is a more fundamental change,
7177        it seems better to set the default behaviour to "off" (as it is more
7178        natural) and make it configurable in the level editor (as a property
7179        of gem style elements). Already existing converted levels (neither
7180        private nor contributed levels) are changed to the new behaviour. */
7181
7182     if (level->file_version < FILE_VERSION_2_0)
7183       level->em_slippery_gems = TRUE;
7184
7185     return;
7186   }
7187
7188   // ---------- use game engine the level was created with --------------------
7189
7190   /* For all levels which are not forced to use the latest game engine
7191      version (normally user contributed, private and undefined levels),
7192      use the version of the game engine the levels were created for.
7193
7194      Since 2.0.1, the game engine version is now directly stored
7195      in the level file (chunk "VERS"), so there is no need anymore
7196      to set the game version from the file version (except for old,
7197      pre-2.0 levels, where the game version is still taken from the
7198      file format version used to store the level -- see above). */
7199
7200   // player was faster than enemies in 1.0.0 and before
7201   if (level->file_version == FILE_VERSION_1_0)
7202     for (i = 0; i < MAX_PLAYERS; i++)
7203       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7204
7205   // default behaviour for EM style gems was "slippery" only in 2.0.1
7206   if (level->game_version == VERSION_IDENT(2,0,1,0))
7207     level->em_slippery_gems = TRUE;
7208
7209   // springs could be pushed over pits before (pre-release version) 2.2.0
7210   if (level->game_version < VERSION_IDENT(2,2,0,0))
7211     level->use_spring_bug = TRUE;
7212
7213   if (level->game_version < VERSION_IDENT(3,2,0,5))
7214   {
7215     // time orb caused limited time in endless time levels before 3.2.0-5
7216     level->use_time_orb_bug = TRUE;
7217
7218     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7219     level->block_snap_field = FALSE;
7220
7221     // extra time score was same value as time left score before 3.2.0-5
7222     level->extra_time_score = level->score[SC_TIME_BONUS];
7223   }
7224
7225   if (level->game_version < VERSION_IDENT(3,2,0,7))
7226   {
7227     // default behaviour for snapping was "not continuous" before 3.2.0-7
7228     level->continuous_snapping = FALSE;
7229   }
7230
7231   // only few elements were able to actively move into acid before 3.1.0
7232   // trigger settings did not exist before 3.1.0; set to default "any"
7233   if (level->game_version < VERSION_IDENT(3,1,0,0))
7234   {
7235     // correct "can move into acid" settings (all zero in old levels)
7236
7237     level->can_move_into_acid_bits = 0; // nothing can move into acid
7238     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7239
7240     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7241     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7242     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7243     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7244
7245     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7246       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7247
7248     // correct trigger settings (stored as zero == "none" in old levels)
7249
7250     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7251     {
7252       int element = EL_CUSTOM_START + i;
7253       struct ElementInfo *ei = &element_info[element];
7254
7255       for (j = 0; j < ei->num_change_pages; j++)
7256       {
7257         struct ElementChangeInfo *change = &ei->change_page[j];
7258
7259         change->trigger_player = CH_PLAYER_ANY;
7260         change->trigger_page = CH_PAGE_ANY;
7261       }
7262     }
7263   }
7264
7265   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7266   {
7267     int element = EL_CUSTOM_256;
7268     struct ElementInfo *ei = &element_info[element];
7269     struct ElementChangeInfo *change = &ei->change_page[0];
7270
7271     /* This is needed to fix a problem that was caused by a bugfix in function
7272        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7273        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7274        not replace walkable elements, but instead just placed the player on it,
7275        without placing the Sokoban field under the player). Unfortunately, this
7276        breaks "Snake Bite" style levels when the snake is halfway through a door
7277        that just closes (the snake head is still alive and can be moved in this
7278        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7279        player (without Sokoban element) which then gets killed as designed). */
7280
7281     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7282          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7283         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7284       change->target_element = EL_PLAYER_1;
7285   }
7286
7287   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7288   if (level->game_version < VERSION_IDENT(3,2,5,0))
7289   {
7290     /* This is needed to fix a problem that was caused by a bugfix in function
7291        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7292        corrects the behaviour when a custom element changes to another custom
7293        element with a higher element number that has change actions defined.
7294        Normally, only one change per frame is allowed for custom elements.
7295        Therefore, it is checked if a custom element already changed in the
7296        current frame; if it did, subsequent changes are suppressed.
7297        Unfortunately, this is only checked for element changes, but not for
7298        change actions, which are still executed. As the function above loops
7299        through all custom elements from lower to higher, an element change
7300        resulting in a lower CE number won't be checked again, while a target
7301        element with a higher number will also be checked, and potential change
7302        actions will get executed for this CE, too (which is wrong), while
7303        further changes are ignored (which is correct). As this bugfix breaks
7304        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7305        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7306        behaviour for existing levels and tapes that make use of this bug */
7307
7308     level->use_action_after_change_bug = TRUE;
7309   }
7310
7311   // not centering level after relocating player was default only in 3.2.3
7312   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7313     level->shifted_relocation = TRUE;
7314
7315   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7316   if (level->game_version < VERSION_IDENT(3,2,6,0))
7317     level->em_explodes_by_fire = TRUE;
7318
7319   // levels were solved by the first player entering an exit up to 4.1.0.0
7320   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7321     level->solved_by_one_player = TRUE;
7322
7323   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7324   if (level->game_version < VERSION_IDENT(4,1,1,1))
7325     level->use_life_bugs = TRUE;
7326
7327   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7328   if (level->game_version < VERSION_IDENT(4,1,1,1))
7329     level->sb_objects_needed = FALSE;
7330
7331   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7332   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7333     level->finish_dig_collect = FALSE;
7334
7335   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7336   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7337     level->keep_walkable_ce = TRUE;
7338 }
7339
7340 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7341 {
7342   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7343   int x, y;
7344
7345   // check if this level is (not) a Sokoban level
7346   for (y = 0; y < level->fieldy; y++)
7347     for (x = 0; x < level->fieldx; x++)
7348       if (!IS_SB_ELEMENT(Tile[x][y]))
7349         is_sokoban_level = FALSE;
7350
7351   if (is_sokoban_level)
7352   {
7353     // set special level settings for Sokoban levels
7354     SetLevelSettings_SB(level);
7355   }
7356 }
7357
7358 static void LoadLevel_InitSettings(struct LevelInfo *level)
7359 {
7360   // adjust level settings for (non-native) Sokoban-style levels
7361   LoadLevel_InitSettings_SB(level);
7362
7363   // rename levels with title "nameless level" or if renaming is forced
7364   if (leveldir_current->empty_level_name != NULL &&
7365       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7366        leveldir_current->force_level_name))
7367     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7368              leveldir_current->empty_level_name, level_nr);
7369 }
7370
7371 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7372 {
7373   int i, x, y;
7374
7375   // map elements that have changed in newer versions
7376   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7377                                                     level->game_version);
7378   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7379     for (x = 0; x < 3; x++)
7380       for (y = 0; y < 3; y++)
7381         level->yamyam_content[i].e[x][y] =
7382           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7383                                     level->game_version);
7384
7385 }
7386
7387 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7388 {
7389   int i, j;
7390
7391   // map custom element change events that have changed in newer versions
7392   // (these following values were accidentally changed in version 3.0.1)
7393   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7394   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7395   {
7396     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7397     {
7398       int element = EL_CUSTOM_START + i;
7399
7400       // order of checking and copying events to be mapped is important
7401       // (do not change the start and end value -- they are constant)
7402       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7403       {
7404         if (HAS_CHANGE_EVENT(element, j - 2))
7405         {
7406           SET_CHANGE_EVENT(element, j - 2, FALSE);
7407           SET_CHANGE_EVENT(element, j, TRUE);
7408         }
7409       }
7410
7411       // order of checking and copying events to be mapped is important
7412       // (do not change the start and end value -- they are constant)
7413       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7414       {
7415         if (HAS_CHANGE_EVENT(element, j - 1))
7416         {
7417           SET_CHANGE_EVENT(element, j - 1, FALSE);
7418           SET_CHANGE_EVENT(element, j, TRUE);
7419         }
7420       }
7421     }
7422   }
7423
7424   // initialize "can_change" field for old levels with only one change page
7425   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7426   {
7427     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7428     {
7429       int element = EL_CUSTOM_START + i;
7430
7431       if (CAN_CHANGE(element))
7432         element_info[element].change->can_change = TRUE;
7433     }
7434   }
7435
7436   // correct custom element values (for old levels without these options)
7437   if (level->game_version < VERSION_IDENT(3,1,1,0))
7438   {
7439     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7440     {
7441       int element = EL_CUSTOM_START + i;
7442       struct ElementInfo *ei = &element_info[element];
7443
7444       if (ei->access_direction == MV_NO_DIRECTION)
7445         ei->access_direction = MV_ALL_DIRECTIONS;
7446     }
7447   }
7448
7449   // correct custom element values (fix invalid values for all versions)
7450   if (1)
7451   {
7452     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7453     {
7454       int element = EL_CUSTOM_START + i;
7455       struct ElementInfo *ei = &element_info[element];
7456
7457       for (j = 0; j < ei->num_change_pages; j++)
7458       {
7459         struct ElementChangeInfo *change = &ei->change_page[j];
7460
7461         if (change->trigger_player == CH_PLAYER_NONE)
7462           change->trigger_player = CH_PLAYER_ANY;
7463
7464         if (change->trigger_side == CH_SIDE_NONE)
7465           change->trigger_side = CH_SIDE_ANY;
7466       }
7467     }
7468   }
7469
7470   // initialize "can_explode" field for old levels which did not store this
7471   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7472   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7473   {
7474     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7475     {
7476       int element = EL_CUSTOM_START + i;
7477
7478       if (EXPLODES_1X1_OLD(element))
7479         element_info[element].explosion_type = EXPLODES_1X1;
7480
7481       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7482                                              EXPLODES_SMASHED(element) ||
7483                                              EXPLODES_IMPACT(element)));
7484     }
7485   }
7486
7487   // correct previously hard-coded move delay values for maze runner style
7488   if (level->game_version < VERSION_IDENT(3,1,1,0))
7489   {
7490     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7491     {
7492       int element = EL_CUSTOM_START + i;
7493
7494       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7495       {
7496         // previously hard-coded and therefore ignored
7497         element_info[element].move_delay_fixed = 9;
7498         element_info[element].move_delay_random = 0;
7499       }
7500     }
7501   }
7502
7503   // set some other uninitialized values of custom elements in older levels
7504   if (level->game_version < VERSION_IDENT(3,1,0,0))
7505   {
7506     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7507     {
7508       int element = EL_CUSTOM_START + i;
7509
7510       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7511
7512       element_info[element].explosion_delay = 17;
7513       element_info[element].ignition_delay = 8;
7514     }
7515   }
7516
7517   // set mouse click change events to work for left/middle/right mouse button
7518   if (level->game_version < VERSION_IDENT(4,2,3,0))
7519   {
7520     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7521     {
7522       int element = EL_CUSTOM_START + i;
7523       struct ElementInfo *ei = &element_info[element];
7524
7525       for (j = 0; j < ei->num_change_pages; j++)
7526       {
7527         struct ElementChangeInfo *change = &ei->change_page[j];
7528
7529         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7530             change->has_event[CE_PRESSED_BY_MOUSE] ||
7531             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7532             change->has_event[CE_MOUSE_PRESSED_ON_X])
7533           change->trigger_side = CH_SIDE_ANY;
7534       }
7535     }
7536   }
7537 }
7538
7539 static void LoadLevel_InitElements(struct LevelInfo *level)
7540 {
7541   LoadLevel_InitStandardElements(level);
7542
7543   if (level->file_has_custom_elements)
7544     LoadLevel_InitCustomElements(level);
7545
7546   // initialize element properties for level editor etc.
7547   InitElementPropertiesEngine(level->game_version);
7548   InitElementPropertiesGfxElement();
7549 }
7550
7551 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7552 {
7553   int x, y;
7554
7555   // map elements that have changed in newer versions
7556   for (y = 0; y < level->fieldy; y++)
7557     for (x = 0; x < level->fieldx; x++)
7558       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7559                                                      level->game_version);
7560
7561   // clear unused playfield data (nicer if level gets resized in editor)
7562   for (x = 0; x < MAX_LEV_FIELDX; x++)
7563     for (y = 0; y < MAX_LEV_FIELDY; y++)
7564       if (x >= level->fieldx || y >= level->fieldy)
7565         level->field[x][y] = EL_EMPTY;
7566
7567   // copy elements to runtime playfield array
7568   for (x = 0; x < MAX_LEV_FIELDX; x++)
7569     for (y = 0; y < MAX_LEV_FIELDY; y++)
7570       Tile[x][y] = level->field[x][y];
7571
7572   // initialize level size variables for faster access
7573   lev_fieldx = level->fieldx;
7574   lev_fieldy = level->fieldy;
7575
7576   // determine border element for this level
7577   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7578     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7579   else
7580     SetBorderElement();
7581 }
7582
7583 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7584 {
7585   struct LevelFileInfo *level_file_info = &level->file_info;
7586
7587   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7588     CopyNativeLevel_RND_to_Native(level);
7589 }
7590
7591 static void LoadLevelTemplate_LoadAndInit(void)
7592 {
7593   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7594
7595   LoadLevel_InitVersion(&level_template);
7596   LoadLevel_InitElements(&level_template);
7597   LoadLevel_InitSettings(&level_template);
7598
7599   ActivateLevelTemplate();
7600 }
7601
7602 void LoadLevelTemplate(int nr)
7603 {
7604   if (!fileExists(getGlobalLevelTemplateFilename()))
7605   {
7606     Warn("no level template found for this level");
7607
7608     return;
7609   }
7610
7611   setLevelFileInfo(&level_template.file_info, nr);
7612
7613   LoadLevelTemplate_LoadAndInit();
7614 }
7615
7616 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7617 {
7618   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7619
7620   LoadLevelTemplate_LoadAndInit();
7621 }
7622
7623 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7624 {
7625   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7626
7627   if (level.use_custom_template)
7628   {
7629     if (network_level != NULL)
7630       LoadNetworkLevelTemplate(network_level);
7631     else
7632       LoadLevelTemplate(-1);
7633   }
7634
7635   LoadLevel_InitVersion(&level);
7636   LoadLevel_InitElements(&level);
7637   LoadLevel_InitPlayfield(&level);
7638   LoadLevel_InitSettings(&level);
7639
7640   LoadLevel_InitNativeEngines(&level);
7641 }
7642
7643 void LoadLevel(int nr)
7644 {
7645   SetLevelSetInfo(leveldir_current->identifier, nr);
7646
7647   setLevelFileInfo(&level.file_info, nr);
7648
7649   LoadLevel_LoadAndInit(NULL);
7650 }
7651
7652 void LoadLevelInfoOnly(int nr)
7653 {
7654   setLevelFileInfo(&level.file_info, nr);
7655
7656   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7657 }
7658
7659 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7660 {
7661   SetLevelSetInfo(network_level->leveldir_identifier,
7662                   network_level->file_info.nr);
7663
7664   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7665
7666   LoadLevel_LoadAndInit(network_level);
7667 }
7668
7669 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7670 {
7671   int chunk_size = 0;
7672
7673   chunk_size += putFileVersion(file, level->file_version);
7674   chunk_size += putFileVersion(file, level->game_version);
7675
7676   return chunk_size;
7677 }
7678
7679 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7680 {
7681   int chunk_size = 0;
7682
7683   chunk_size += putFile16BitBE(file, level->creation_date.year);
7684   chunk_size += putFile8Bit(file,    level->creation_date.month);
7685   chunk_size += putFile8Bit(file,    level->creation_date.day);
7686
7687   return chunk_size;
7688 }
7689
7690 #if ENABLE_HISTORIC_CHUNKS
7691 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7692 {
7693   int i, x, y;
7694
7695   putFile8Bit(file, level->fieldx);
7696   putFile8Bit(file, level->fieldy);
7697
7698   putFile16BitBE(file, level->time);
7699   putFile16BitBE(file, level->gems_needed);
7700
7701   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7702     putFile8Bit(file, level->name[i]);
7703
7704   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7705     putFile8Bit(file, level->score[i]);
7706
7707   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7708     for (y = 0; y < 3; y++)
7709       for (x = 0; x < 3; x++)
7710         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7711                            level->yamyam_content[i].e[x][y]));
7712   putFile8Bit(file, level->amoeba_speed);
7713   putFile8Bit(file, level->time_magic_wall);
7714   putFile8Bit(file, level->time_wheel);
7715   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7716                      level->amoeba_content));
7717   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7718   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7719   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7720   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7721
7722   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7723
7724   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7725   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7726   putFile32BitBE(file, level->can_move_into_acid_bits);
7727   putFile8Bit(file, level->dont_collide_with_bits);
7728
7729   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7730   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7731
7732   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7733   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7734   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7735
7736   putFile8Bit(file, level->game_engine_type);
7737
7738   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7739 }
7740 #endif
7741
7742 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7743 {
7744   int chunk_size = 0;
7745   int i;
7746
7747   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7748     chunk_size += putFile8Bit(file, level->name[i]);
7749
7750   return chunk_size;
7751 }
7752
7753 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7754 {
7755   int chunk_size = 0;
7756   int i;
7757
7758   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7759     chunk_size += putFile8Bit(file, level->author[i]);
7760
7761   return chunk_size;
7762 }
7763
7764 #if ENABLE_HISTORIC_CHUNKS
7765 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7766 {
7767   int chunk_size = 0;
7768   int x, y;
7769
7770   for (y = 0; y < level->fieldy; y++)
7771     for (x = 0; x < level->fieldx; x++)
7772       if (level->encoding_16bit_field)
7773         chunk_size += putFile16BitBE(file, level->field[x][y]);
7774       else
7775         chunk_size += putFile8Bit(file, level->field[x][y]);
7776
7777   return chunk_size;
7778 }
7779 #endif
7780
7781 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7782 {
7783   int chunk_size = 0;
7784   int x, y;
7785
7786   for (y = 0; y < level->fieldy; y++) 
7787     for (x = 0; x < level->fieldx; x++) 
7788       chunk_size += putFile16BitBE(file, level->field[x][y]);
7789
7790   return chunk_size;
7791 }
7792
7793 #if ENABLE_HISTORIC_CHUNKS
7794 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7795 {
7796   int i, x, y;
7797
7798   putFile8Bit(file, EL_YAMYAM);
7799   putFile8Bit(file, level->num_yamyam_contents);
7800   putFile8Bit(file, 0);
7801   putFile8Bit(file, 0);
7802
7803   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7804     for (y = 0; y < 3; y++)
7805       for (x = 0; x < 3; x++)
7806         if (level->encoding_16bit_field)
7807           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7808         else
7809           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7810 }
7811 #endif
7812
7813 #if ENABLE_HISTORIC_CHUNKS
7814 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7815 {
7816   int i, x, y;
7817   int num_contents, content_xsize, content_ysize;
7818   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7819
7820   if (element == EL_YAMYAM)
7821   {
7822     num_contents = level->num_yamyam_contents;
7823     content_xsize = 3;
7824     content_ysize = 3;
7825
7826     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7827       for (y = 0; y < 3; y++)
7828         for (x = 0; x < 3; x++)
7829           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7830   }
7831   else if (element == EL_BD_AMOEBA)
7832   {
7833     num_contents = 1;
7834     content_xsize = 1;
7835     content_ysize = 1;
7836
7837     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7838       for (y = 0; y < 3; y++)
7839         for (x = 0; x < 3; x++)
7840           content_array[i][x][y] = EL_EMPTY;
7841     content_array[0][0][0] = level->amoeba_content;
7842   }
7843   else
7844   {
7845     // chunk header already written -- write empty chunk data
7846     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7847
7848     Warn("cannot save content for element '%d'", element);
7849
7850     return;
7851   }
7852
7853   putFile16BitBE(file, element);
7854   putFile8Bit(file, num_contents);
7855   putFile8Bit(file, content_xsize);
7856   putFile8Bit(file, content_ysize);
7857
7858   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7859
7860   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7861     for (y = 0; y < 3; y++)
7862       for (x = 0; x < 3; x++)
7863         putFile16BitBE(file, content_array[i][x][y]);
7864 }
7865 #endif
7866
7867 #if ENABLE_HISTORIC_CHUNKS
7868 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7869 {
7870   int envelope_nr = element - EL_ENVELOPE_1;
7871   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7872   int chunk_size = 0;
7873   int i;
7874
7875   chunk_size += putFile16BitBE(file, element);
7876   chunk_size += putFile16BitBE(file, envelope_len);
7877   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7878   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7879
7880   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7881   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7882
7883   for (i = 0; i < envelope_len; i++)
7884     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7885
7886   return chunk_size;
7887 }
7888 #endif
7889
7890 #if ENABLE_HISTORIC_CHUNKS
7891 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7892                            int num_changed_custom_elements)
7893 {
7894   int i, check = 0;
7895
7896   putFile16BitBE(file, num_changed_custom_elements);
7897
7898   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7899   {
7900     int element = EL_CUSTOM_START + i;
7901
7902     struct ElementInfo *ei = &element_info[element];
7903
7904     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7905     {
7906       if (check < num_changed_custom_elements)
7907       {
7908         putFile16BitBE(file, element);
7909         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7910       }
7911
7912       check++;
7913     }
7914   }
7915
7916   if (check != num_changed_custom_elements)     // should not happen
7917     Warn("inconsistent number of custom element properties");
7918 }
7919 #endif
7920
7921 #if ENABLE_HISTORIC_CHUNKS
7922 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7923                            int num_changed_custom_elements)
7924 {
7925   int i, check = 0;
7926
7927   putFile16BitBE(file, num_changed_custom_elements);
7928
7929   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7930   {
7931     int element = EL_CUSTOM_START + i;
7932
7933     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7934     {
7935       if (check < num_changed_custom_elements)
7936       {
7937         putFile16BitBE(file, element);
7938         putFile16BitBE(file, element_info[element].change->target_element);
7939       }
7940
7941       check++;
7942     }
7943   }
7944
7945   if (check != num_changed_custom_elements)     // should not happen
7946     Warn("inconsistent number of custom target elements");
7947 }
7948 #endif
7949
7950 #if ENABLE_HISTORIC_CHUNKS
7951 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7952                            int num_changed_custom_elements)
7953 {
7954   int i, j, x, y, check = 0;
7955
7956   putFile16BitBE(file, num_changed_custom_elements);
7957
7958   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7959   {
7960     int element = EL_CUSTOM_START + i;
7961     struct ElementInfo *ei = &element_info[element];
7962
7963     if (ei->modified_settings)
7964     {
7965       if (check < num_changed_custom_elements)
7966       {
7967         putFile16BitBE(file, element);
7968
7969         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7970           putFile8Bit(file, ei->description[j]);
7971
7972         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7973
7974         // some free bytes for future properties and padding
7975         WriteUnusedBytesToFile(file, 7);
7976
7977         putFile8Bit(file, ei->use_gfx_element);
7978         putFile16BitBE(file, ei->gfx_element_initial);
7979
7980         putFile8Bit(file, ei->collect_score_initial);
7981         putFile8Bit(file, ei->collect_count_initial);
7982
7983         putFile16BitBE(file, ei->push_delay_fixed);
7984         putFile16BitBE(file, ei->push_delay_random);
7985         putFile16BitBE(file, ei->move_delay_fixed);
7986         putFile16BitBE(file, ei->move_delay_random);
7987
7988         putFile16BitBE(file, ei->move_pattern);
7989         putFile8Bit(file, ei->move_direction_initial);
7990         putFile8Bit(file, ei->move_stepsize);
7991
7992         for (y = 0; y < 3; y++)
7993           for (x = 0; x < 3; x++)
7994             putFile16BitBE(file, ei->content.e[x][y]);
7995
7996         putFile32BitBE(file, ei->change->events);
7997
7998         putFile16BitBE(file, ei->change->target_element);
7999
8000         putFile16BitBE(file, ei->change->delay_fixed);
8001         putFile16BitBE(file, ei->change->delay_random);
8002         putFile16BitBE(file, ei->change->delay_frames);
8003
8004         putFile16BitBE(file, ei->change->initial_trigger_element);
8005
8006         putFile8Bit(file, ei->change->explode);
8007         putFile8Bit(file, ei->change->use_target_content);
8008         putFile8Bit(file, ei->change->only_if_complete);
8009         putFile8Bit(file, ei->change->use_random_replace);
8010
8011         putFile8Bit(file, ei->change->random_percentage);
8012         putFile8Bit(file, ei->change->replace_when);
8013
8014         for (y = 0; y < 3; y++)
8015           for (x = 0; x < 3; x++)
8016             putFile16BitBE(file, ei->change->content.e[x][y]);
8017
8018         putFile8Bit(file, ei->slippery_type);
8019
8020         // some free bytes for future properties and padding
8021         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8022       }
8023
8024       check++;
8025     }
8026   }
8027
8028   if (check != num_changed_custom_elements)     // should not happen
8029     Warn("inconsistent number of custom element properties");
8030 }
8031 #endif
8032
8033 #if ENABLE_HISTORIC_CHUNKS
8034 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8035 {
8036   struct ElementInfo *ei = &element_info[element];
8037   int i, j, x, y;
8038
8039   // ---------- custom element base property values (96 bytes) ----------------
8040
8041   putFile16BitBE(file, element);
8042
8043   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8044     putFile8Bit(file, ei->description[i]);
8045
8046   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8047
8048   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8049
8050   putFile8Bit(file, ei->num_change_pages);
8051
8052   putFile16BitBE(file, ei->ce_value_fixed_initial);
8053   putFile16BitBE(file, ei->ce_value_random_initial);
8054   putFile8Bit(file, ei->use_last_ce_value);
8055
8056   putFile8Bit(file, ei->use_gfx_element);
8057   putFile16BitBE(file, ei->gfx_element_initial);
8058
8059   putFile8Bit(file, ei->collect_score_initial);
8060   putFile8Bit(file, ei->collect_count_initial);
8061
8062   putFile8Bit(file, ei->drop_delay_fixed);
8063   putFile8Bit(file, ei->push_delay_fixed);
8064   putFile8Bit(file, ei->drop_delay_random);
8065   putFile8Bit(file, ei->push_delay_random);
8066   putFile16BitBE(file, ei->move_delay_fixed);
8067   putFile16BitBE(file, ei->move_delay_random);
8068
8069   // bits 0 - 15 of "move_pattern" ...
8070   putFile16BitBE(file, ei->move_pattern & 0xffff);
8071   putFile8Bit(file, ei->move_direction_initial);
8072   putFile8Bit(file, ei->move_stepsize);
8073
8074   putFile8Bit(file, ei->slippery_type);
8075
8076   for (y = 0; y < 3; y++)
8077     for (x = 0; x < 3; x++)
8078       putFile16BitBE(file, ei->content.e[x][y]);
8079
8080   putFile16BitBE(file, ei->move_enter_element);
8081   putFile16BitBE(file, ei->move_leave_element);
8082   putFile8Bit(file, ei->move_leave_type);
8083
8084   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8085   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8086
8087   putFile8Bit(file, ei->access_direction);
8088
8089   putFile8Bit(file, ei->explosion_delay);
8090   putFile8Bit(file, ei->ignition_delay);
8091   putFile8Bit(file, ei->explosion_type);
8092
8093   // some free bytes for future custom property values and padding
8094   WriteUnusedBytesToFile(file, 1);
8095
8096   // ---------- change page property values (48 bytes) ------------------------
8097
8098   for (i = 0; i < ei->num_change_pages; i++)
8099   {
8100     struct ElementChangeInfo *change = &ei->change_page[i];
8101     unsigned int event_bits;
8102
8103     // bits 0 - 31 of "has_event[]" ...
8104     event_bits = 0;
8105     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8106       if (change->has_event[j])
8107         event_bits |= (1u << j);
8108     putFile32BitBE(file, event_bits);
8109
8110     putFile16BitBE(file, change->target_element);
8111
8112     putFile16BitBE(file, change->delay_fixed);
8113     putFile16BitBE(file, change->delay_random);
8114     putFile16BitBE(file, change->delay_frames);
8115
8116     putFile16BitBE(file, change->initial_trigger_element);
8117
8118     putFile8Bit(file, change->explode);
8119     putFile8Bit(file, change->use_target_content);
8120     putFile8Bit(file, change->only_if_complete);
8121     putFile8Bit(file, change->use_random_replace);
8122
8123     putFile8Bit(file, change->random_percentage);
8124     putFile8Bit(file, change->replace_when);
8125
8126     for (y = 0; y < 3; y++)
8127       for (x = 0; x < 3; x++)
8128         putFile16BitBE(file, change->target_content.e[x][y]);
8129
8130     putFile8Bit(file, change->can_change);
8131
8132     putFile8Bit(file, change->trigger_side);
8133
8134     putFile8Bit(file, change->trigger_player);
8135     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8136                        log_2(change->trigger_page)));
8137
8138     putFile8Bit(file, change->has_action);
8139     putFile8Bit(file, change->action_type);
8140     putFile8Bit(file, change->action_mode);
8141     putFile16BitBE(file, change->action_arg);
8142
8143     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8144     event_bits = 0;
8145     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8146       if (change->has_event[j])
8147         event_bits |= (1u << (j - 32));
8148     putFile8Bit(file, event_bits);
8149   }
8150 }
8151 #endif
8152
8153 #if ENABLE_HISTORIC_CHUNKS
8154 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8155 {
8156   struct ElementInfo *ei = &element_info[element];
8157   struct ElementGroupInfo *group = ei->group;
8158   int i;
8159
8160   putFile16BitBE(file, element);
8161
8162   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8163     putFile8Bit(file, ei->description[i]);
8164
8165   putFile8Bit(file, group->num_elements);
8166
8167   putFile8Bit(file, ei->use_gfx_element);
8168   putFile16BitBE(file, ei->gfx_element_initial);
8169
8170   putFile8Bit(file, group->choice_mode);
8171
8172   // some free bytes for future values and padding
8173   WriteUnusedBytesToFile(file, 3);
8174
8175   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8176     putFile16BitBE(file, group->element[i]);
8177 }
8178 #endif
8179
8180 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8181                                 boolean write_element)
8182 {
8183   int save_type = entry->save_type;
8184   int data_type = entry->data_type;
8185   int conf_type = entry->conf_type;
8186   int byte_mask = conf_type & CONF_MASK_BYTES;
8187   int element = entry->element;
8188   int default_value = entry->default_value;
8189   int num_bytes = 0;
8190   boolean modified = FALSE;
8191
8192   if (byte_mask != CONF_MASK_MULTI_BYTES)
8193   {
8194     void *value_ptr = entry->value;
8195     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8196                  *(int *)value_ptr);
8197
8198     // check if any settings have been modified before saving them
8199     if (value != default_value)
8200       modified = TRUE;
8201
8202     // do not save if explicitly told or if unmodified default settings
8203     if ((save_type == SAVE_CONF_NEVER) ||
8204         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8205       return 0;
8206
8207     if (write_element)
8208       num_bytes += putFile16BitBE(file, element);
8209
8210     num_bytes += putFile8Bit(file, conf_type);
8211     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8212                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8213                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8214                   0);
8215   }
8216   else if (data_type == TYPE_STRING)
8217   {
8218     char *default_string = entry->default_string;
8219     char *string = (char *)(entry->value);
8220     int string_length = strlen(string);
8221     int i;
8222
8223     // check if any settings have been modified before saving them
8224     if (!strEqual(string, default_string))
8225       modified = TRUE;
8226
8227     // do not save if explicitly told or if unmodified default settings
8228     if ((save_type == SAVE_CONF_NEVER) ||
8229         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8230       return 0;
8231
8232     if (write_element)
8233       num_bytes += putFile16BitBE(file, element);
8234
8235     num_bytes += putFile8Bit(file, conf_type);
8236     num_bytes += putFile16BitBE(file, string_length);
8237
8238     for (i = 0; i < string_length; i++)
8239       num_bytes += putFile8Bit(file, string[i]);
8240   }
8241   else if (data_type == TYPE_ELEMENT_LIST)
8242   {
8243     int *element_array = (int *)(entry->value);
8244     int num_elements = *(int *)(entry->num_entities);
8245     int i;
8246
8247     // check if any settings have been modified before saving them
8248     for (i = 0; i < num_elements; i++)
8249       if (element_array[i] != default_value)
8250         modified = TRUE;
8251
8252     // do not save if explicitly told or if unmodified default settings
8253     if ((save_type == SAVE_CONF_NEVER) ||
8254         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8255       return 0;
8256
8257     if (write_element)
8258       num_bytes += putFile16BitBE(file, element);
8259
8260     num_bytes += putFile8Bit(file, conf_type);
8261     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8262
8263     for (i = 0; i < num_elements; i++)
8264       num_bytes += putFile16BitBE(file, element_array[i]);
8265   }
8266   else if (data_type == TYPE_CONTENT_LIST)
8267   {
8268     struct Content *content = (struct Content *)(entry->value);
8269     int num_contents = *(int *)(entry->num_entities);
8270     int i, x, y;
8271
8272     // check if any settings have been modified before saving them
8273     for (i = 0; i < num_contents; i++)
8274       for (y = 0; y < 3; y++)
8275         for (x = 0; x < 3; x++)
8276           if (content[i].e[x][y] != default_value)
8277             modified = TRUE;
8278
8279     // do not save if explicitly told or if unmodified default settings
8280     if ((save_type == SAVE_CONF_NEVER) ||
8281         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8282       return 0;
8283
8284     if (write_element)
8285       num_bytes += putFile16BitBE(file, element);
8286
8287     num_bytes += putFile8Bit(file, conf_type);
8288     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8289
8290     for (i = 0; i < num_contents; i++)
8291       for (y = 0; y < 3; y++)
8292         for (x = 0; x < 3; x++)
8293           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8294   }
8295
8296   return num_bytes;
8297 }
8298
8299 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8300 {
8301   int chunk_size = 0;
8302   int i;
8303
8304   li = *level;          // copy level data into temporary buffer
8305
8306   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8307     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8308
8309   return chunk_size;
8310 }
8311
8312 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8313 {
8314   int chunk_size = 0;
8315   int i;
8316
8317   li = *level;          // copy level data into temporary buffer
8318
8319   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8320     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8321
8322   return chunk_size;
8323 }
8324
8325 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8326 {
8327   int envelope_nr = element - EL_ENVELOPE_1;
8328   int chunk_size = 0;
8329   int i;
8330
8331   chunk_size += putFile16BitBE(file, element);
8332
8333   // copy envelope data into temporary buffer
8334   xx_envelope = level->envelope[envelope_nr];
8335
8336   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8337     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8338
8339   return chunk_size;
8340 }
8341
8342 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8343 {
8344   struct ElementInfo *ei = &element_info[element];
8345   int chunk_size = 0;
8346   int i, j;
8347
8348   chunk_size += putFile16BitBE(file, element);
8349
8350   xx_ei = *ei;          // copy element data into temporary buffer
8351
8352   // set default description string for this specific element
8353   strcpy(xx_default_description, getDefaultElementDescription(ei));
8354
8355   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8356     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8357
8358   for (i = 0; i < ei->num_change_pages; i++)
8359   {
8360     struct ElementChangeInfo *change = &ei->change_page[i];
8361
8362     xx_current_change_page = i;
8363
8364     xx_change = *change;        // copy change data into temporary buffer
8365
8366     resetEventBits();
8367     setEventBitsFromEventFlags(change);
8368
8369     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8370       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8371                                          FALSE);
8372   }
8373
8374   return chunk_size;
8375 }
8376
8377 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8378 {
8379   struct ElementInfo *ei = &element_info[element];
8380   struct ElementGroupInfo *group = ei->group;
8381   int chunk_size = 0;
8382   int i;
8383
8384   chunk_size += putFile16BitBE(file, element);
8385
8386   xx_ei = *ei;          // copy element data into temporary buffer
8387   xx_group = *group;    // copy group data into temporary buffer
8388
8389   // set default description string for this specific element
8390   strcpy(xx_default_description, getDefaultElementDescription(ei));
8391
8392   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8393     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8394
8395   return chunk_size;
8396 }
8397
8398 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8399 {
8400   struct ElementInfo *ei = &element_info[element];
8401   int chunk_size = 0;
8402   int i;
8403
8404   chunk_size += putFile16BitBE(file, element);
8405
8406   xx_ei = *ei;          // copy element data into temporary buffer
8407
8408   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8409     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8410
8411   return chunk_size;
8412 }
8413
8414 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8415                                   boolean save_as_template)
8416 {
8417   int chunk_size;
8418   int i;
8419   FILE *file;
8420
8421   if (!(file = fopen(filename, MODE_WRITE)))
8422   {
8423     Warn("cannot save level file '%s'", filename);
8424
8425     return;
8426   }
8427
8428   level->file_version = FILE_VERSION_ACTUAL;
8429   level->game_version = GAME_VERSION_ACTUAL;
8430
8431   level->creation_date = getCurrentDate();
8432
8433   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8434   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8435
8436   chunk_size = SaveLevel_VERS(NULL, level);
8437   putFileChunkBE(file, "VERS", chunk_size);
8438   SaveLevel_VERS(file, level);
8439
8440   chunk_size = SaveLevel_DATE(NULL, level);
8441   putFileChunkBE(file, "DATE", chunk_size);
8442   SaveLevel_DATE(file, level);
8443
8444   chunk_size = SaveLevel_NAME(NULL, level);
8445   putFileChunkBE(file, "NAME", chunk_size);
8446   SaveLevel_NAME(file, level);
8447
8448   chunk_size = SaveLevel_AUTH(NULL, level);
8449   putFileChunkBE(file, "AUTH", chunk_size);
8450   SaveLevel_AUTH(file, level);
8451
8452   chunk_size = SaveLevel_INFO(NULL, level);
8453   putFileChunkBE(file, "INFO", chunk_size);
8454   SaveLevel_INFO(file, level);
8455
8456   chunk_size = SaveLevel_BODY(NULL, level);
8457   putFileChunkBE(file, "BODY", chunk_size);
8458   SaveLevel_BODY(file, level);
8459
8460   chunk_size = SaveLevel_ELEM(NULL, level);
8461   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8462   {
8463     putFileChunkBE(file, "ELEM", chunk_size);
8464     SaveLevel_ELEM(file, level);
8465   }
8466
8467   for (i = 0; i < NUM_ENVELOPES; i++)
8468   {
8469     int element = EL_ENVELOPE_1 + i;
8470
8471     chunk_size = SaveLevel_NOTE(NULL, level, element);
8472     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8473     {
8474       putFileChunkBE(file, "NOTE", chunk_size);
8475       SaveLevel_NOTE(file, level, element);
8476     }
8477   }
8478
8479   // if not using template level, check for non-default custom/group elements
8480   if (!level->use_custom_template || save_as_template)
8481   {
8482     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8483     {
8484       int element = EL_CUSTOM_START + i;
8485
8486       chunk_size = SaveLevel_CUSX(NULL, level, element);
8487       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8488       {
8489         putFileChunkBE(file, "CUSX", chunk_size);
8490         SaveLevel_CUSX(file, level, element);
8491       }
8492     }
8493
8494     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8495     {
8496       int element = EL_GROUP_START + i;
8497
8498       chunk_size = SaveLevel_GRPX(NULL, level, element);
8499       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8500       {
8501         putFileChunkBE(file, "GRPX", chunk_size);
8502         SaveLevel_GRPX(file, level, element);
8503       }
8504     }
8505
8506     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8507     {
8508       int element = GET_EMPTY_ELEMENT(i);
8509
8510       chunk_size = SaveLevel_EMPX(NULL, level, element);
8511       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8512       {
8513         putFileChunkBE(file, "EMPX", chunk_size);
8514         SaveLevel_EMPX(file, level, element);
8515       }
8516     }
8517   }
8518
8519   fclose(file);
8520
8521   SetFilePermissions(filename, PERMS_PRIVATE);
8522 }
8523
8524 void SaveLevel(int nr)
8525 {
8526   char *filename = getDefaultLevelFilename(nr);
8527
8528   SaveLevelFromFilename(&level, filename, FALSE);
8529 }
8530
8531 void SaveLevelTemplate(void)
8532 {
8533   char *filename = getLocalLevelTemplateFilename();
8534
8535   SaveLevelFromFilename(&level, filename, TRUE);
8536 }
8537
8538 boolean SaveLevelChecked(int nr)
8539 {
8540   char *filename = getDefaultLevelFilename(nr);
8541   boolean new_level = !fileExists(filename);
8542   boolean level_saved = FALSE;
8543
8544   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8545   {
8546     SaveLevel(nr);
8547
8548     if (new_level)
8549       Request("Level saved!", REQ_CONFIRM);
8550
8551     level_saved = TRUE;
8552   }
8553
8554   return level_saved;
8555 }
8556
8557 void DumpLevel(struct LevelInfo *level)
8558 {
8559   if (level->no_level_file || level->no_valid_file)
8560   {
8561     Warn("cannot dump -- no valid level file found");
8562
8563     return;
8564   }
8565
8566   PrintLine("-", 79);
8567   Print("Level xxx (file version %08d, game version %08d)\n",
8568         level->file_version, level->game_version);
8569   PrintLine("-", 79);
8570
8571   Print("Level author: '%s'\n", level->author);
8572   Print("Level title:  '%s'\n", level->name);
8573   Print("\n");
8574   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8575   Print("\n");
8576   Print("Level time:  %d seconds\n", level->time);
8577   Print("Gems needed: %d\n", level->gems_needed);
8578   Print("\n");
8579   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8580   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8581   Print("Time for light:      %d seconds\n", level->time_light);
8582   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8583   Print("\n");
8584   Print("Amoeba speed: %d\n", level->amoeba_speed);
8585   Print("\n");
8586
8587   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8588   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8589   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8590   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8591   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8592   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8593
8594   if (options.debug)
8595   {
8596     int i, j;
8597
8598     for (i = 0; i < NUM_ENVELOPES; i++)
8599     {
8600       char *text = level->envelope[i].text;
8601       int text_len = strlen(text);
8602       boolean has_text = FALSE;
8603
8604       for (j = 0; j < text_len; j++)
8605         if (text[j] != ' ' && text[j] != '\n')
8606           has_text = TRUE;
8607
8608       if (has_text)
8609       {
8610         Print("\n");
8611         Print("Envelope %d:\n'%s'\n", i + 1, text);
8612       }
8613     }
8614   }
8615
8616   PrintLine("-", 79);
8617 }
8618
8619 void DumpLevels(void)
8620 {
8621   static LevelDirTree *dumplevel_leveldir = NULL;
8622
8623   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8624                                                  global.dumplevel_leveldir);
8625
8626   if (dumplevel_leveldir == NULL)
8627     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8628
8629   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8630       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8631     Fail("no such level number: %d", global.dumplevel_level_nr);
8632
8633   leveldir_current = dumplevel_leveldir;
8634
8635   LoadLevel(global.dumplevel_level_nr);
8636   DumpLevel(&level);
8637
8638   CloseAllAndExit(0);
8639 }
8640
8641
8642 // ============================================================================
8643 // tape file functions
8644 // ============================================================================
8645
8646 static void setTapeInfoToDefaults(void)
8647 {
8648   int i;
8649
8650   // always start with reliable default values (empty tape)
8651   TapeErase();
8652
8653   // default values (also for pre-1.2 tapes) with only the first player
8654   tape.player_participates[0] = TRUE;
8655   for (i = 1; i < MAX_PLAYERS; i++)
8656     tape.player_participates[i] = FALSE;
8657
8658   // at least one (default: the first) player participates in every tape
8659   tape.num_participating_players = 1;
8660
8661   tape.property_bits = TAPE_PROPERTY_NONE;
8662
8663   tape.level_nr = level_nr;
8664   tape.counter = 0;
8665   tape.changed = FALSE;
8666   tape.solved = FALSE;
8667
8668   tape.recording = FALSE;
8669   tape.playing = FALSE;
8670   tape.pausing = FALSE;
8671
8672   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8673   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8674
8675   tape.no_info_chunk = TRUE;
8676   tape.no_valid_file = FALSE;
8677 }
8678
8679 static int getTapePosSize(struct TapeInfo *tape)
8680 {
8681   int tape_pos_size = 0;
8682
8683   if (tape->use_key_actions)
8684     tape_pos_size += tape->num_participating_players;
8685
8686   if (tape->use_mouse_actions)
8687     tape_pos_size += 3;         // x and y position and mouse button mask
8688
8689   tape_pos_size += 1;           // tape action delay value
8690
8691   return tape_pos_size;
8692 }
8693
8694 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8695 {
8696   tape->use_key_actions = FALSE;
8697   tape->use_mouse_actions = FALSE;
8698
8699   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8700     tape->use_key_actions = TRUE;
8701
8702   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8703     tape->use_mouse_actions = TRUE;
8704 }
8705
8706 static int getTapeActionValue(struct TapeInfo *tape)
8707 {
8708   return (tape->use_key_actions &&
8709           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8710           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8711           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8712           TAPE_ACTIONS_DEFAULT);
8713 }
8714
8715 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8716 {
8717   tape->file_version = getFileVersion(file);
8718   tape->game_version = getFileVersion(file);
8719
8720   return chunk_size;
8721 }
8722
8723 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8724 {
8725   int i;
8726
8727   tape->random_seed = getFile32BitBE(file);
8728   tape->date        = getFile32BitBE(file);
8729   tape->length      = getFile32BitBE(file);
8730
8731   // read header fields that are new since version 1.2
8732   if (tape->file_version >= FILE_VERSION_1_2)
8733   {
8734     byte store_participating_players = getFile8Bit(file);
8735     int engine_version;
8736
8737     // since version 1.2, tapes store which players participate in the tape
8738     tape->num_participating_players = 0;
8739     for (i = 0; i < MAX_PLAYERS; i++)
8740     {
8741       tape->player_participates[i] = FALSE;
8742
8743       if (store_participating_players & (1 << i))
8744       {
8745         tape->player_participates[i] = TRUE;
8746         tape->num_participating_players++;
8747       }
8748     }
8749
8750     setTapeActionFlags(tape, getFile8Bit(file));
8751
8752     tape->property_bits = getFile8Bit(file);
8753     tape->solved = getFile8Bit(file);
8754
8755     engine_version = getFileVersion(file);
8756     if (engine_version > 0)
8757       tape->engine_version = engine_version;
8758     else
8759       tape->engine_version = tape->game_version;
8760   }
8761
8762   return chunk_size;
8763 }
8764
8765 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8766 {
8767   tape->scr_fieldx = getFile8Bit(file);
8768   tape->scr_fieldy = getFile8Bit(file);
8769
8770   return chunk_size;
8771 }
8772
8773 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8774 {
8775   char *level_identifier = NULL;
8776   int level_identifier_size;
8777   int i;
8778
8779   tape->no_info_chunk = FALSE;
8780
8781   level_identifier_size = getFile16BitBE(file);
8782
8783   level_identifier = checked_malloc(level_identifier_size);
8784
8785   for (i = 0; i < level_identifier_size; i++)
8786     level_identifier[i] = getFile8Bit(file);
8787
8788   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8789   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8790
8791   checked_free(level_identifier);
8792
8793   tape->level_nr = getFile16BitBE(file);
8794
8795   chunk_size = 2 + level_identifier_size + 2;
8796
8797   return chunk_size;
8798 }
8799
8800 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8801 {
8802   int i, j;
8803   int tape_pos_size = getTapePosSize(tape);
8804   int chunk_size_expected = tape_pos_size * tape->length;
8805
8806   if (chunk_size_expected != chunk_size)
8807   {
8808     ReadUnusedBytesFromFile(file, chunk_size);
8809     return chunk_size_expected;
8810   }
8811
8812   for (i = 0; i < tape->length; i++)
8813   {
8814     if (i >= MAX_TAPE_LEN)
8815     {
8816       Warn("tape truncated -- size exceeds maximum tape size %d",
8817             MAX_TAPE_LEN);
8818
8819       // tape too large; read and ignore remaining tape data from this chunk
8820       for (;i < tape->length; i++)
8821         ReadUnusedBytesFromFile(file, tape_pos_size);
8822
8823       break;
8824     }
8825
8826     if (tape->use_key_actions)
8827     {
8828       for (j = 0; j < MAX_PLAYERS; j++)
8829       {
8830         tape->pos[i].action[j] = MV_NONE;
8831
8832         if (tape->player_participates[j])
8833           tape->pos[i].action[j] = getFile8Bit(file);
8834       }
8835     }
8836
8837     if (tape->use_mouse_actions)
8838     {
8839       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8840       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8841       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8842     }
8843
8844     tape->pos[i].delay = getFile8Bit(file);
8845
8846     if (tape->file_version == FILE_VERSION_1_0)
8847     {
8848       // eliminate possible diagonal moves in old tapes
8849       // this is only for backward compatibility
8850
8851       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8852       byte action = tape->pos[i].action[0];
8853       int k, num_moves = 0;
8854
8855       for (k = 0; k < 4; k++)
8856       {
8857         if (action & joy_dir[k])
8858         {
8859           tape->pos[i + num_moves].action[0] = joy_dir[k];
8860           if (num_moves > 0)
8861             tape->pos[i + num_moves].delay = 0;
8862           num_moves++;
8863         }
8864       }
8865
8866       if (num_moves > 1)
8867       {
8868         num_moves--;
8869         i += num_moves;
8870         tape->length += num_moves;
8871       }
8872     }
8873     else if (tape->file_version < FILE_VERSION_2_0)
8874     {
8875       // convert pre-2.0 tapes to new tape format
8876
8877       if (tape->pos[i].delay > 1)
8878       {
8879         // action part
8880         tape->pos[i + 1] = tape->pos[i];
8881         tape->pos[i + 1].delay = 1;
8882
8883         // delay part
8884         for (j = 0; j < MAX_PLAYERS; j++)
8885           tape->pos[i].action[j] = MV_NONE;
8886         tape->pos[i].delay--;
8887
8888         i++;
8889         tape->length++;
8890       }
8891     }
8892
8893     if (checkEndOfFile(file))
8894       break;
8895   }
8896
8897   if (i != tape->length)
8898     chunk_size = tape_pos_size * i;
8899
8900   return chunk_size;
8901 }
8902
8903 static void LoadTape_SokobanSolution(char *filename)
8904 {
8905   File *file;
8906   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8907
8908   if (!(file = openFile(filename, MODE_READ)))
8909   {
8910     tape.no_valid_file = TRUE;
8911
8912     return;
8913   }
8914
8915   while (!checkEndOfFile(file))
8916   {
8917     unsigned char c = getByteFromFile(file);
8918
8919     if (checkEndOfFile(file))
8920       break;
8921
8922     switch (c)
8923     {
8924       case 'u':
8925       case 'U':
8926         tape.pos[tape.length].action[0] = MV_UP;
8927         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8928         tape.length++;
8929         break;
8930
8931       case 'd':
8932       case 'D':
8933         tape.pos[tape.length].action[0] = MV_DOWN;
8934         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8935         tape.length++;
8936         break;
8937
8938       case 'l':
8939       case 'L':
8940         tape.pos[tape.length].action[0] = MV_LEFT;
8941         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8942         tape.length++;
8943         break;
8944
8945       case 'r':
8946       case 'R':
8947         tape.pos[tape.length].action[0] = MV_RIGHT;
8948         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8949         tape.length++;
8950         break;
8951
8952       case '\n':
8953       case '\r':
8954       case '\t':
8955       case ' ':
8956         // ignore white-space characters
8957         break;
8958
8959       default:
8960         tape.no_valid_file = TRUE;
8961
8962         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8963
8964         break;
8965     }
8966   }
8967
8968   closeFile(file);
8969
8970   if (tape.no_valid_file)
8971     return;
8972
8973   tape.length_frames  = GetTapeLengthFrames();
8974   tape.length_seconds = GetTapeLengthSeconds();
8975 }
8976
8977 void LoadTapeFromFilename(char *filename)
8978 {
8979   char cookie[MAX_LINE_LEN];
8980   char chunk_name[CHUNK_ID_LEN + 1];
8981   File *file;
8982   int chunk_size;
8983
8984   // always start with reliable default values
8985   setTapeInfoToDefaults();
8986
8987   if (strSuffix(filename, ".sln"))
8988   {
8989     LoadTape_SokobanSolution(filename);
8990
8991     return;
8992   }
8993
8994   if (!(file = openFile(filename, MODE_READ)))
8995   {
8996     tape.no_valid_file = TRUE;
8997
8998     return;
8999   }
9000
9001   getFileChunkBE(file, chunk_name, NULL);
9002   if (strEqual(chunk_name, "RND1"))
9003   {
9004     getFile32BitBE(file);               // not used
9005
9006     getFileChunkBE(file, chunk_name, NULL);
9007     if (!strEqual(chunk_name, "TAPE"))
9008     {
9009       tape.no_valid_file = TRUE;
9010
9011       Warn("unknown format of tape file '%s'", filename);
9012
9013       closeFile(file);
9014
9015       return;
9016     }
9017   }
9018   else  // check for pre-2.0 file format with cookie string
9019   {
9020     strcpy(cookie, chunk_name);
9021     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9022       cookie[4] = '\0';
9023     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9024       cookie[strlen(cookie) - 1] = '\0';
9025
9026     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9027     {
9028       tape.no_valid_file = TRUE;
9029
9030       Warn("unknown format of tape file '%s'", filename);
9031
9032       closeFile(file);
9033
9034       return;
9035     }
9036
9037     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9038     {
9039       tape.no_valid_file = TRUE;
9040
9041       Warn("unsupported version of tape file '%s'", filename);
9042
9043       closeFile(file);
9044
9045       return;
9046     }
9047
9048     // pre-2.0 tape files have no game version, so use file version here
9049     tape.game_version = tape.file_version;
9050   }
9051
9052   if (tape.file_version < FILE_VERSION_1_2)
9053   {
9054     // tape files from versions before 1.2.0 without chunk structure
9055     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9056     LoadTape_BODY(file, 2 * tape.length,      &tape);
9057   }
9058   else
9059   {
9060     static struct
9061     {
9062       char *name;
9063       int size;
9064       int (*loader)(File *, int, struct TapeInfo *);
9065     }
9066     chunk_info[] =
9067     {
9068       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9069       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9070       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9071       { "INFO", -1,                     LoadTape_INFO },
9072       { "BODY", -1,                     LoadTape_BODY },
9073       {  NULL,  0,                      NULL }
9074     };
9075
9076     while (getFileChunkBE(file, chunk_name, &chunk_size))
9077     {
9078       int i = 0;
9079
9080       while (chunk_info[i].name != NULL &&
9081              !strEqual(chunk_name, chunk_info[i].name))
9082         i++;
9083
9084       if (chunk_info[i].name == NULL)
9085       {
9086         Warn("unknown chunk '%s' in tape file '%s'",
9087               chunk_name, filename);
9088
9089         ReadUnusedBytesFromFile(file, chunk_size);
9090       }
9091       else if (chunk_info[i].size != -1 &&
9092                chunk_info[i].size != chunk_size)
9093       {
9094         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9095               chunk_size, chunk_name, filename);
9096
9097         ReadUnusedBytesFromFile(file, chunk_size);
9098       }
9099       else
9100       {
9101         // call function to load this tape chunk
9102         int chunk_size_expected =
9103           (chunk_info[i].loader)(file, chunk_size, &tape);
9104
9105         // the size of some chunks cannot be checked before reading other
9106         // chunks first (like "HEAD" and "BODY") that contain some header
9107         // information, so check them here
9108         if (chunk_size_expected != chunk_size)
9109         {
9110           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9111                 chunk_size, chunk_name, filename);
9112         }
9113       }
9114     }
9115   }
9116
9117   closeFile(file);
9118
9119   tape.length_frames  = GetTapeLengthFrames();
9120   tape.length_seconds = GetTapeLengthSeconds();
9121
9122 #if 0
9123   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9124         tape.file_version);
9125   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9126         tape.game_version);
9127   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9128         tape.engine_version);
9129 #endif
9130 }
9131
9132 void LoadTape(int nr)
9133 {
9134   char *filename = getTapeFilename(nr);
9135
9136   LoadTapeFromFilename(filename);
9137 }
9138
9139 void LoadSolutionTape(int nr)
9140 {
9141   char *filename = getSolutionTapeFilename(nr);
9142
9143   LoadTapeFromFilename(filename);
9144
9145   if (TAPE_IS_EMPTY(tape))
9146   {
9147     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9148         level.native_bd_level->replay != NULL)
9149       CopyNativeTape_BD_to_RND(&level);
9150     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9151         level.native_sp_level->demo.is_available)
9152       CopyNativeTape_SP_to_RND(&level);
9153   }
9154 }
9155
9156 void LoadScoreTape(char *score_tape_basename, int nr)
9157 {
9158   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9159
9160   LoadTapeFromFilename(filename);
9161 }
9162
9163 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9164 {
9165   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9166
9167   LoadTapeFromFilename(filename);
9168 }
9169
9170 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9171 {
9172   // chunk required for team mode tapes with non-default screen size
9173   return (tape->num_participating_players > 1 &&
9174           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9175            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9176 }
9177
9178 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9179 {
9180   putFileVersion(file, tape->file_version);
9181   putFileVersion(file, tape->game_version);
9182 }
9183
9184 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9185 {
9186   int i;
9187   byte store_participating_players = 0;
9188
9189   // set bits for participating players for compact storage
9190   for (i = 0; i < MAX_PLAYERS; i++)
9191     if (tape->player_participates[i])
9192       store_participating_players |= (1 << i);
9193
9194   putFile32BitBE(file, tape->random_seed);
9195   putFile32BitBE(file, tape->date);
9196   putFile32BitBE(file, tape->length);
9197
9198   putFile8Bit(file, store_participating_players);
9199
9200   putFile8Bit(file, getTapeActionValue(tape));
9201
9202   putFile8Bit(file, tape->property_bits);
9203   putFile8Bit(file, tape->solved);
9204
9205   putFileVersion(file, tape->engine_version);
9206 }
9207
9208 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9209 {
9210   putFile8Bit(file, tape->scr_fieldx);
9211   putFile8Bit(file, tape->scr_fieldy);
9212 }
9213
9214 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9215 {
9216   int level_identifier_size = strlen(tape->level_identifier) + 1;
9217   int i;
9218
9219   putFile16BitBE(file, level_identifier_size);
9220
9221   for (i = 0; i < level_identifier_size; i++)
9222     putFile8Bit(file, tape->level_identifier[i]);
9223
9224   putFile16BitBE(file, tape->level_nr);
9225 }
9226
9227 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9228 {
9229   int i, j;
9230
9231   for (i = 0; i < tape->length; i++)
9232   {
9233     if (tape->use_key_actions)
9234     {
9235       for (j = 0; j < MAX_PLAYERS; j++)
9236         if (tape->player_participates[j])
9237           putFile8Bit(file, tape->pos[i].action[j]);
9238     }
9239
9240     if (tape->use_mouse_actions)
9241     {
9242       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9243       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9244       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9245     }
9246
9247     putFile8Bit(file, tape->pos[i].delay);
9248   }
9249 }
9250
9251 void SaveTapeToFilename(char *filename)
9252 {
9253   FILE *file;
9254   int tape_pos_size;
9255   int info_chunk_size;
9256   int body_chunk_size;
9257
9258   if (!(file = fopen(filename, MODE_WRITE)))
9259   {
9260     Warn("cannot save level recording file '%s'", filename);
9261
9262     return;
9263   }
9264
9265   tape_pos_size = getTapePosSize(&tape);
9266
9267   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9268   body_chunk_size = tape_pos_size * tape.length;
9269
9270   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9271   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9272
9273   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9274   SaveTape_VERS(file, &tape);
9275
9276   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9277   SaveTape_HEAD(file, &tape);
9278
9279   if (checkSaveTape_SCRN(&tape))
9280   {
9281     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9282     SaveTape_SCRN(file, &tape);
9283   }
9284
9285   putFileChunkBE(file, "INFO", info_chunk_size);
9286   SaveTape_INFO(file, &tape);
9287
9288   putFileChunkBE(file, "BODY", body_chunk_size);
9289   SaveTape_BODY(file, &tape);
9290
9291   fclose(file);
9292
9293   SetFilePermissions(filename, PERMS_PRIVATE);
9294 }
9295
9296 static void SaveTapeExt(char *filename)
9297 {
9298   int i;
9299
9300   tape.file_version = FILE_VERSION_ACTUAL;
9301   tape.game_version = GAME_VERSION_ACTUAL;
9302
9303   tape.num_participating_players = 0;
9304
9305   // count number of participating players
9306   for (i = 0; i < MAX_PLAYERS; i++)
9307     if (tape.player_participates[i])
9308       tape.num_participating_players++;
9309
9310   SaveTapeToFilename(filename);
9311
9312   tape.changed = FALSE;
9313 }
9314
9315 void SaveTape(int nr)
9316 {
9317   char *filename = getTapeFilename(nr);
9318
9319   InitTapeDirectory(leveldir_current->subdir);
9320
9321   SaveTapeExt(filename);
9322 }
9323
9324 void SaveScoreTape(int nr)
9325 {
9326   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9327
9328   // used instead of "leveldir_current->subdir" (for network games)
9329   InitScoreTapeDirectory(levelset.identifier, nr);
9330
9331   SaveTapeExt(filename);
9332 }
9333
9334 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9335                                   unsigned int req_state_added)
9336 {
9337   char *filename = getTapeFilename(nr);
9338   boolean new_tape = !fileExists(filename);
9339   boolean tape_saved = FALSE;
9340
9341   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9342   {
9343     SaveTape(nr);
9344
9345     if (new_tape)
9346       Request(msg_saved, REQ_CONFIRM | req_state_added);
9347
9348     tape_saved = TRUE;
9349   }
9350
9351   return tape_saved;
9352 }
9353
9354 boolean SaveTapeChecked(int nr)
9355 {
9356   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9357 }
9358
9359 boolean SaveTapeChecked_LevelSolved(int nr)
9360 {
9361   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9362                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9363 }
9364
9365 void DumpTape(struct TapeInfo *tape)
9366 {
9367   int tape_frame_counter;
9368   int i, j;
9369
9370   if (tape->no_valid_file)
9371   {
9372     Warn("cannot dump -- no valid tape file found");
9373
9374     return;
9375   }
9376
9377   PrintLine("-", 79);
9378
9379   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9380         tape->level_nr, tape->file_version, tape->game_version);
9381   Print("                  (effective engine version %08d)\n",
9382         tape->engine_version);
9383   Print("Level series identifier: '%s'\n", tape->level_identifier);
9384
9385   Print("Solution tape: %s\n",
9386         tape->solved ? "yes" :
9387         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9388
9389   Print("Special tape properties: ");
9390   if (tape->property_bits == TAPE_PROPERTY_NONE)
9391     Print("[none]");
9392   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9393     Print("[em_random_bug]");
9394   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9395     Print("[game_speed]");
9396   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9397     Print("[pause]");
9398   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9399     Print("[single_step]");
9400   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9401     Print("[snapshot]");
9402   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9403     Print("[replayed]");
9404   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9405     Print("[tas_keys]");
9406   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9407     Print("[small_graphics]");
9408   Print("\n");
9409
9410   int year2 = tape->date / 10000;
9411   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9412   int month_index_raw = (tape->date / 100) % 100;
9413   int month_index = month_index_raw % 12;       // prevent invalid index
9414   int month = month_index + 1;
9415   int day = tape->date % 100;
9416
9417   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9418
9419   PrintLine("-", 79);
9420
9421   tape_frame_counter = 0;
9422
9423   for (i = 0; i < tape->length; i++)
9424   {
9425     if (i >= MAX_TAPE_LEN)
9426       break;
9427
9428     Print("%04d: ", i);
9429
9430     for (j = 0; j < MAX_PLAYERS; j++)
9431     {
9432       if (tape->player_participates[j])
9433       {
9434         int action = tape->pos[i].action[j];
9435
9436         Print("%d:%02x ", j, action);
9437         Print("[%c%c%c%c|%c%c] - ",
9438               (action & JOY_LEFT ? '<' : ' '),
9439               (action & JOY_RIGHT ? '>' : ' '),
9440               (action & JOY_UP ? '^' : ' '),
9441               (action & JOY_DOWN ? 'v' : ' '),
9442               (action & JOY_BUTTON_1 ? '1' : ' '),
9443               (action & JOY_BUTTON_2 ? '2' : ' '));
9444       }
9445     }
9446
9447     Print("(%03d) ", tape->pos[i].delay);
9448     Print("[%05d]\n", tape_frame_counter);
9449
9450     tape_frame_counter += tape->pos[i].delay;
9451   }
9452
9453   PrintLine("-", 79);
9454 }
9455
9456 void DumpTapes(void)
9457 {
9458   static LevelDirTree *dumptape_leveldir = NULL;
9459
9460   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9461                                                 global.dumptape_leveldir);
9462
9463   if (dumptape_leveldir == NULL)
9464     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9465
9466   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9467       global.dumptape_level_nr > dumptape_leveldir->last_level)
9468     Fail("no such level number: %d", global.dumptape_level_nr);
9469
9470   leveldir_current = dumptape_leveldir;
9471
9472   if (options.mytapes)
9473     LoadTape(global.dumptape_level_nr);
9474   else
9475     LoadSolutionTape(global.dumptape_level_nr);
9476
9477   DumpTape(&tape);
9478
9479   CloseAllAndExit(0);
9480 }
9481
9482
9483 // ============================================================================
9484 // score file functions
9485 // ============================================================================
9486
9487 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9488 {
9489   int i;
9490
9491   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9492   {
9493     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9494     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9495     scores->entry[i].score = 0;
9496     scores->entry[i].time = 0;
9497
9498     scores->entry[i].id = -1;
9499     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9500     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9501     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9502     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9503     strcpy(scores->entry[i].country_code, "??");
9504   }
9505
9506   scores->num_entries = 0;
9507   scores->last_added = -1;
9508   scores->last_added_local = -1;
9509
9510   scores->updated = FALSE;
9511   scores->uploaded = FALSE;
9512   scores->tape_downloaded = FALSE;
9513   scores->force_last_added = FALSE;
9514
9515   // The following values are intentionally not reset here:
9516   // - last_level_nr
9517   // - last_entry_nr
9518   // - next_level_nr
9519   // - continue_playing
9520   // - continue_on_return
9521 }
9522
9523 static void setScoreInfoToDefaults(void)
9524 {
9525   setScoreInfoToDefaultsExt(&scores);
9526 }
9527
9528 static void setServerScoreInfoToDefaults(void)
9529 {
9530   setScoreInfoToDefaultsExt(&server_scores);
9531 }
9532
9533 static void LoadScore_OLD(int nr)
9534 {
9535   int i;
9536   char *filename = getScoreFilename(nr);
9537   char cookie[MAX_LINE_LEN];
9538   char line[MAX_LINE_LEN];
9539   char *line_ptr;
9540   FILE *file;
9541
9542   if (!(file = fopen(filename, MODE_READ)))
9543     return;
9544
9545   // check file identifier
9546   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9547     cookie[0] = '\0';
9548   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9549     cookie[strlen(cookie) - 1] = '\0';
9550
9551   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9552   {
9553     Warn("unknown format of score file '%s'", filename);
9554
9555     fclose(file);
9556
9557     return;
9558   }
9559
9560   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9561   {
9562     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9563       Warn("fscanf() failed; %s", strerror(errno));
9564
9565     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9566       line[0] = '\0';
9567
9568     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9569       line[strlen(line) - 1] = '\0';
9570
9571     for (line_ptr = line; *line_ptr; line_ptr++)
9572     {
9573       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9574       {
9575         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9576         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9577         break;
9578       }
9579     }
9580   }
9581
9582   fclose(file);
9583 }
9584
9585 static void ConvertScore_OLD(void)
9586 {
9587   // only convert score to time for levels that rate playing time over score
9588   if (!level.rate_time_over_score)
9589     return;
9590
9591   // convert old score to playing time for score-less levels (like Supaplex)
9592   int time_final_max = 999;
9593   int i;
9594
9595   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9596   {
9597     int score = scores.entry[i].score;
9598
9599     if (score > 0 && score < time_final_max)
9600       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9601   }
9602 }
9603
9604 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9605 {
9606   scores->file_version = getFileVersion(file);
9607   scores->game_version = getFileVersion(file);
9608
9609   return chunk_size;
9610 }
9611
9612 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9613 {
9614   char *level_identifier = NULL;
9615   int level_identifier_size;
9616   int i;
9617
9618   level_identifier_size = getFile16BitBE(file);
9619
9620   level_identifier = checked_malloc(level_identifier_size);
9621
9622   for (i = 0; i < level_identifier_size; i++)
9623     level_identifier[i] = getFile8Bit(file);
9624
9625   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9626   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9627
9628   checked_free(level_identifier);
9629
9630   scores->level_nr = getFile16BitBE(file);
9631   scores->num_entries = getFile16BitBE(file);
9632
9633   chunk_size = 2 + level_identifier_size + 2 + 2;
9634
9635   return chunk_size;
9636 }
9637
9638 static int LoadScore_NAME(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_PLAYER_NAME_LEN; j++)
9645       scores->entry[i].name[j] = getFile8Bit(file);
9646
9647     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9648   }
9649
9650   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9651
9652   return chunk_size;
9653 }
9654
9655 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9656 {
9657   int i;
9658
9659   for (i = 0; i < scores->num_entries; i++)
9660     scores->entry[i].score = getFile16BitBE(file);
9661
9662   chunk_size = scores->num_entries * 2;
9663
9664   return chunk_size;
9665 }
9666
9667 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9668 {
9669   int i;
9670
9671   for (i = 0; i < scores->num_entries; i++)
9672     scores->entry[i].score = getFile32BitBE(file);
9673
9674   chunk_size = scores->num_entries * 4;
9675
9676   return chunk_size;
9677 }
9678
9679 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9680 {
9681   int i;
9682
9683   for (i = 0; i < scores->num_entries; i++)
9684     scores->entry[i].time = getFile32BitBE(file);
9685
9686   chunk_size = scores->num_entries * 4;
9687
9688   return chunk_size;
9689 }
9690
9691 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9692 {
9693   int i, j;
9694
9695   for (i = 0; i < scores->num_entries; i++)
9696   {
9697     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9698       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9699
9700     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9701   }
9702
9703   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9704
9705   return chunk_size;
9706 }
9707
9708 void LoadScore(int nr)
9709 {
9710   char *filename = getScoreFilename(nr);
9711   char cookie[MAX_LINE_LEN];
9712   char chunk_name[CHUNK_ID_LEN + 1];
9713   int chunk_size;
9714   boolean old_score_file_format = FALSE;
9715   File *file;
9716
9717   // always start with reliable default values
9718   setScoreInfoToDefaults();
9719
9720   if (!(file = openFile(filename, MODE_READ)))
9721     return;
9722
9723   getFileChunkBE(file, chunk_name, NULL);
9724   if (strEqual(chunk_name, "RND1"))
9725   {
9726     getFile32BitBE(file);               // not used
9727
9728     getFileChunkBE(file, chunk_name, NULL);
9729     if (!strEqual(chunk_name, "SCOR"))
9730     {
9731       Warn("unknown format of score file '%s'", filename);
9732
9733       closeFile(file);
9734
9735       return;
9736     }
9737   }
9738   else  // check for old file format with cookie string
9739   {
9740     strcpy(cookie, chunk_name);
9741     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9742       cookie[4] = '\0';
9743     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9744       cookie[strlen(cookie) - 1] = '\0';
9745
9746     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9747     {
9748       Warn("unknown format of score file '%s'", filename);
9749
9750       closeFile(file);
9751
9752       return;
9753     }
9754
9755     old_score_file_format = TRUE;
9756   }
9757
9758   if (old_score_file_format)
9759   {
9760     // score files from versions before 4.2.4.0 without chunk structure
9761     LoadScore_OLD(nr);
9762
9763     // convert score to time, if possible (mainly for Supaplex levels)
9764     ConvertScore_OLD();
9765   }
9766   else
9767   {
9768     static struct
9769     {
9770       char *name;
9771       int size;
9772       int (*loader)(File *, int, struct ScoreInfo *);
9773     }
9774     chunk_info[] =
9775     {
9776       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9777       { "INFO", -1,                     LoadScore_INFO },
9778       { "NAME", -1,                     LoadScore_NAME },
9779       { "SCOR", -1,                     LoadScore_SCOR },
9780       { "SC4R", -1,                     LoadScore_SC4R },
9781       { "TIME", -1,                     LoadScore_TIME },
9782       { "TAPE", -1,                     LoadScore_TAPE },
9783
9784       {  NULL,  0,                      NULL }
9785     };
9786
9787     while (getFileChunkBE(file, chunk_name, &chunk_size))
9788     {
9789       int i = 0;
9790
9791       while (chunk_info[i].name != NULL &&
9792              !strEqual(chunk_name, chunk_info[i].name))
9793         i++;
9794
9795       if (chunk_info[i].name == NULL)
9796       {
9797         Warn("unknown chunk '%s' in score file '%s'",
9798               chunk_name, filename);
9799
9800         ReadUnusedBytesFromFile(file, chunk_size);
9801       }
9802       else if (chunk_info[i].size != -1 &&
9803                chunk_info[i].size != chunk_size)
9804       {
9805         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9806               chunk_size, chunk_name, filename);
9807
9808         ReadUnusedBytesFromFile(file, chunk_size);
9809       }
9810       else
9811       {
9812         // call function to load this score chunk
9813         int chunk_size_expected =
9814           (chunk_info[i].loader)(file, chunk_size, &scores);
9815
9816         // the size of some chunks cannot be checked before reading other
9817         // chunks first (like "HEAD" and "BODY") that contain some header
9818         // information, so check them here
9819         if (chunk_size_expected != chunk_size)
9820         {
9821           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9822                 chunk_size, chunk_name, filename);
9823         }
9824       }
9825     }
9826   }
9827
9828   closeFile(file);
9829 }
9830
9831 #if ENABLE_HISTORIC_CHUNKS
9832 void SaveScore_OLD(int nr)
9833 {
9834   int i;
9835   char *filename = getScoreFilename(nr);
9836   FILE *file;
9837
9838   // used instead of "leveldir_current->subdir" (for network games)
9839   InitScoreDirectory(levelset.identifier);
9840
9841   if (!(file = fopen(filename, MODE_WRITE)))
9842   {
9843     Warn("cannot save score for level %d", nr);
9844
9845     return;
9846   }
9847
9848   fprintf(file, "%s\n\n", SCORE_COOKIE);
9849
9850   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9851     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9852
9853   fclose(file);
9854
9855   SetFilePermissions(filename, PERMS_PRIVATE);
9856 }
9857 #endif
9858
9859 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9860 {
9861   putFileVersion(file, scores->file_version);
9862   putFileVersion(file, scores->game_version);
9863 }
9864
9865 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9866 {
9867   int level_identifier_size = strlen(scores->level_identifier) + 1;
9868   int i;
9869
9870   putFile16BitBE(file, level_identifier_size);
9871
9872   for (i = 0; i < level_identifier_size; i++)
9873     putFile8Bit(file, scores->level_identifier[i]);
9874
9875   putFile16BitBE(file, scores->level_nr);
9876   putFile16BitBE(file, scores->num_entries);
9877 }
9878
9879 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9880 {
9881   int i, j;
9882
9883   for (i = 0; i < scores->num_entries; i++)
9884   {
9885     int name_size = strlen(scores->entry[i].name);
9886
9887     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9888       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9889   }
9890 }
9891
9892 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9893 {
9894   int i;
9895
9896   for (i = 0; i < scores->num_entries; i++)
9897     putFile16BitBE(file, scores->entry[i].score);
9898 }
9899
9900 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9901 {
9902   int i;
9903
9904   for (i = 0; i < scores->num_entries; i++)
9905     putFile32BitBE(file, scores->entry[i].score);
9906 }
9907
9908 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9909 {
9910   int i;
9911
9912   for (i = 0; i < scores->num_entries; i++)
9913     putFile32BitBE(file, scores->entry[i].time);
9914 }
9915
9916 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9917 {
9918   int i, j;
9919
9920   for (i = 0; i < scores->num_entries; i++)
9921   {
9922     int size = strlen(scores->entry[i].tape_basename);
9923
9924     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9925       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9926   }
9927 }
9928
9929 static void SaveScoreToFilename(char *filename)
9930 {
9931   FILE *file;
9932   int info_chunk_size;
9933   int name_chunk_size;
9934   int scor_chunk_size;
9935   int sc4r_chunk_size;
9936   int time_chunk_size;
9937   int tape_chunk_size;
9938   boolean has_large_score_values;
9939   int i;
9940
9941   if (!(file = fopen(filename, MODE_WRITE)))
9942   {
9943     Warn("cannot save score file '%s'", filename);
9944
9945     return;
9946   }
9947
9948   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9949   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9950   scor_chunk_size = scores.num_entries * 2;
9951   sc4r_chunk_size = scores.num_entries * 4;
9952   time_chunk_size = scores.num_entries * 4;
9953   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9954
9955   has_large_score_values = FALSE;
9956   for (i = 0; i < scores.num_entries; i++)
9957     if (scores.entry[i].score > 0xffff)
9958       has_large_score_values = TRUE;
9959
9960   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9961   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9962
9963   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9964   SaveScore_VERS(file, &scores);
9965
9966   putFileChunkBE(file, "INFO", info_chunk_size);
9967   SaveScore_INFO(file, &scores);
9968
9969   putFileChunkBE(file, "NAME", name_chunk_size);
9970   SaveScore_NAME(file, &scores);
9971
9972   if (has_large_score_values)
9973   {
9974     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9975     SaveScore_SC4R(file, &scores);
9976   }
9977   else
9978   {
9979     putFileChunkBE(file, "SCOR", scor_chunk_size);
9980     SaveScore_SCOR(file, &scores);
9981   }
9982
9983   putFileChunkBE(file, "TIME", time_chunk_size);
9984   SaveScore_TIME(file, &scores);
9985
9986   putFileChunkBE(file, "TAPE", tape_chunk_size);
9987   SaveScore_TAPE(file, &scores);
9988
9989   fclose(file);
9990
9991   SetFilePermissions(filename, PERMS_PRIVATE);
9992 }
9993
9994 void SaveScore(int nr)
9995 {
9996   char *filename = getScoreFilename(nr);
9997   int i;
9998
9999   // used instead of "leveldir_current->subdir" (for network games)
10000   InitScoreDirectory(levelset.identifier);
10001
10002   scores.file_version = FILE_VERSION_ACTUAL;
10003   scores.game_version = GAME_VERSION_ACTUAL;
10004
10005   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10006   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10007   scores.level_nr = level_nr;
10008
10009   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10010     if (scores.entry[i].score == 0 &&
10011         scores.entry[i].time == 0 &&
10012         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10013       break;
10014
10015   scores.num_entries = i;
10016
10017   if (scores.num_entries == 0)
10018     return;
10019
10020   SaveScoreToFilename(filename);
10021 }
10022
10023 static void LoadServerScoreFromCache(int nr)
10024 {
10025   struct ScoreEntry score_entry;
10026   struct
10027   {
10028     void *value;
10029     boolean is_string;
10030     int string_size;
10031   }
10032   score_mapping[] =
10033   {
10034     { &score_entry.score,               FALSE,  0                       },
10035     { &score_entry.time,                FALSE,  0                       },
10036     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10037     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10038     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10039     { &score_entry.id,                  FALSE,  0                       },
10040     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10041     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10042     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10043     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10044
10045     { NULL,                             FALSE,  0                       }
10046   };
10047   char *filename = getScoreCacheFilename(nr);
10048   SetupFileHash *score_hash = loadSetupFileHash(filename);
10049   int i, j;
10050
10051   server_scores.num_entries = 0;
10052
10053   if (score_hash == NULL)
10054     return;
10055
10056   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10057   {
10058     score_entry = server_scores.entry[i];
10059
10060     for (j = 0; score_mapping[j].value != NULL; j++)
10061     {
10062       char token[10];
10063
10064       sprintf(token, "%02d.%d", i, j);
10065
10066       char *value = getHashEntry(score_hash, token);
10067
10068       if (value == NULL)
10069         continue;
10070
10071       if (score_mapping[j].is_string)
10072       {
10073         char *score_value = (char *)score_mapping[j].value;
10074         int value_size = score_mapping[j].string_size;
10075
10076         strncpy(score_value, value, value_size);
10077         score_value[value_size] = '\0';
10078       }
10079       else
10080       {
10081         int *score_value = (int *)score_mapping[j].value;
10082
10083         *score_value = atoi(value);
10084       }
10085
10086       server_scores.num_entries = i + 1;
10087     }
10088
10089     server_scores.entry[i] = score_entry;
10090   }
10091
10092   freeSetupFileHash(score_hash);
10093 }
10094
10095 void LoadServerScore(int nr, boolean download_score)
10096 {
10097   if (!setup.use_api_server)
10098     return;
10099
10100   // always start with reliable default values
10101   setServerScoreInfoToDefaults();
10102
10103   // 1st step: load server scores from cache file (which may not exist)
10104   // (this should prevent reading it while the thread is writing to it)
10105   LoadServerScoreFromCache(nr);
10106
10107   if (download_score && runtime.use_api_server)
10108   {
10109     // 2nd step: download server scores from score server to cache file
10110     // (as thread, as it might time out if the server is not reachable)
10111     ApiGetScoreAsThread(nr);
10112   }
10113 }
10114
10115 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10116 {
10117   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10118
10119   // if score tape not uploaded, ask for uploading missing tapes later
10120   if (!setup.has_remaining_tapes)
10121     setup.ask_for_remaining_tapes = TRUE;
10122
10123   setup.provide_uploading_tapes = TRUE;
10124   setup.has_remaining_tapes = TRUE;
10125
10126   SaveSetup_ServerSetup();
10127 }
10128
10129 void SaveServerScore(int nr, boolean tape_saved)
10130 {
10131   if (!runtime.use_api_server)
10132   {
10133     PrepareScoreTapesForUpload(leveldir_current->subdir);
10134
10135     return;
10136   }
10137
10138   ApiAddScoreAsThread(nr, tape_saved, NULL);
10139 }
10140
10141 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10142                              char *score_tape_filename)
10143 {
10144   if (!runtime.use_api_server)
10145     return;
10146
10147   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10148 }
10149
10150 void LoadLocalAndServerScore(int nr, boolean download_score)
10151 {
10152   int last_added_local = scores.last_added_local;
10153   boolean force_last_added = scores.force_last_added;
10154
10155   // needed if only showing server scores
10156   setScoreInfoToDefaults();
10157
10158   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10159     LoadScore(nr);
10160
10161   // restore last added local score entry (before merging server scores)
10162   scores.last_added = scores.last_added_local = last_added_local;
10163
10164   if (setup.use_api_server &&
10165       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10166   {
10167     // load server scores from cache file and trigger update from server
10168     LoadServerScore(nr, download_score);
10169
10170     // merge local scores with scores from server
10171     MergeServerScore();
10172   }
10173
10174   if (force_last_added)
10175     scores.force_last_added = force_last_added;
10176 }
10177
10178
10179 // ============================================================================
10180 // setup file functions
10181 // ============================================================================
10182
10183 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10184
10185
10186 static struct TokenInfo global_setup_tokens[] =
10187 {
10188   {
10189     TYPE_STRING,
10190     &setup.player_name,                         "player_name"
10191   },
10192   {
10193     TYPE_SWITCH,
10194     &setup.multiple_users,                      "multiple_users"
10195   },
10196   {
10197     TYPE_SWITCH,
10198     &setup.sound,                               "sound"
10199   },
10200   {
10201     TYPE_SWITCH,
10202     &setup.sound_loops,                         "repeating_sound_loops"
10203   },
10204   {
10205     TYPE_SWITCH,
10206     &setup.sound_music,                         "background_music"
10207   },
10208   {
10209     TYPE_SWITCH,
10210     &setup.sound_simple,                        "simple_sound_effects"
10211   },
10212   {
10213     TYPE_SWITCH,
10214     &setup.toons,                               "toons"
10215   },
10216   {
10217     TYPE_SWITCH,
10218     &setup.global_animations,                   "global_animations"
10219   },
10220   {
10221     TYPE_SWITCH,
10222     &setup.scroll_delay,                        "scroll_delay"
10223   },
10224   {
10225     TYPE_SWITCH,
10226     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10227   },
10228   {
10229     TYPE_INTEGER,
10230     &setup.scroll_delay_value,                  "scroll_delay_value"
10231   },
10232   {
10233     TYPE_STRING,
10234     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10235   },
10236   {
10237     TYPE_INTEGER,
10238     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10239   },
10240   {
10241     TYPE_SWITCH,
10242     &setup.fade_screens,                        "fade_screens"
10243   },
10244   {
10245     TYPE_SWITCH,
10246     &setup.autorecord,                          "automatic_tape_recording"
10247   },
10248   {
10249     TYPE_SWITCH,
10250     &setup.autorecord_after_replay,             "autorecord_after_replay"
10251   },
10252   {
10253     TYPE_SWITCH,
10254     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10255   },
10256   {
10257     TYPE_SWITCH,
10258     &setup.show_titlescreen,                    "show_titlescreen"
10259   },
10260   {
10261     TYPE_SWITCH,
10262     &setup.quick_doors,                         "quick_doors"
10263   },
10264   {
10265     TYPE_SWITCH,
10266     &setup.team_mode,                           "team_mode"
10267   },
10268   {
10269     TYPE_SWITCH,
10270     &setup.handicap,                            "handicap"
10271   },
10272   {
10273     TYPE_SWITCH,
10274     &setup.skip_levels,                         "skip_levels"
10275   },
10276   {
10277     TYPE_SWITCH,
10278     &setup.increment_levels,                    "increment_levels"
10279   },
10280   {
10281     TYPE_SWITCH,
10282     &setup.auto_play_next_level,                "auto_play_next_level"
10283   },
10284   {
10285     TYPE_SWITCH,
10286     &setup.count_score_after_game,              "count_score_after_game"
10287   },
10288   {
10289     TYPE_SWITCH,
10290     &setup.show_scores_after_game,              "show_scores_after_game"
10291   },
10292   {
10293     TYPE_SWITCH,
10294     &setup.time_limit,                          "time_limit"
10295   },
10296   {
10297     TYPE_SWITCH,
10298     &setup.fullscreen,                          "fullscreen"
10299   },
10300   {
10301     TYPE_INTEGER,
10302     &setup.window_scaling_percent,              "window_scaling_percent"
10303   },
10304   {
10305     TYPE_STRING,
10306     &setup.window_scaling_quality,              "window_scaling_quality"
10307   },
10308   {
10309     TYPE_STRING,
10310     &setup.screen_rendering_mode,               "screen_rendering_mode"
10311   },
10312   {
10313     TYPE_STRING,
10314     &setup.vsync_mode,                          "vsync_mode"
10315   },
10316   {
10317     TYPE_SWITCH,
10318     &setup.ask_on_escape,                       "ask_on_escape"
10319   },
10320   {
10321     TYPE_SWITCH,
10322     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10323   },
10324   {
10325     TYPE_SWITCH,
10326     &setup.ask_on_game_over,                    "ask_on_game_over"
10327   },
10328   {
10329     TYPE_SWITCH,
10330     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10331   },
10332   {
10333     TYPE_SWITCH,
10334     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10335   },
10336   {
10337     TYPE_SWITCH,
10338     &setup.quick_switch,                        "quick_player_switch"
10339   },
10340   {
10341     TYPE_SWITCH,
10342     &setup.input_on_focus,                      "input_on_focus"
10343   },
10344   {
10345     TYPE_SWITCH,
10346     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10347   },
10348   {
10349     TYPE_SWITCH,
10350     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10351   },
10352   {
10353     TYPE_SWITCH,
10354     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10355   },
10356   {
10357     TYPE_SWITCH,
10358     &setup.game_speed_extended,                 "game_speed_extended"
10359   },
10360   {
10361     TYPE_INTEGER,
10362     &setup.game_frame_delay,                    "game_frame_delay"
10363   },
10364   {
10365     TYPE_SWITCH,
10366     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10367   },
10368   {
10369     TYPE_SWITCH,
10370     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10371   },
10372   {
10373     TYPE_SWITCH,
10374     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10375   },
10376   {
10377     TYPE_SWITCH3,
10378     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10379   },
10380   {
10381     TYPE_SWITCH,
10382     &setup.sp_show_border_elements,             "sp_show_border_elements"
10383   },
10384   {
10385     TYPE_SWITCH,
10386     &setup.small_game_graphics,                 "small_game_graphics"
10387   },
10388   {
10389     TYPE_SWITCH,
10390     &setup.show_load_save_buttons,              "show_load_save_buttons"
10391   },
10392   {
10393     TYPE_SWITCH,
10394     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10395   },
10396   {
10397     TYPE_STRING,
10398     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10399   },
10400   {
10401     TYPE_STRING,
10402     &setup.graphics_set,                        "graphics_set"
10403   },
10404   {
10405     TYPE_STRING,
10406     &setup.sounds_set,                          "sounds_set"
10407   },
10408   {
10409     TYPE_STRING,
10410     &setup.music_set,                           "music_set"
10411   },
10412   {
10413     TYPE_SWITCH3,
10414     &setup.override_level_graphics,             "override_level_graphics"
10415   },
10416   {
10417     TYPE_SWITCH3,
10418     &setup.override_level_sounds,               "override_level_sounds"
10419   },
10420   {
10421     TYPE_SWITCH3,
10422     &setup.override_level_music,                "override_level_music"
10423   },
10424   {
10425     TYPE_INTEGER,
10426     &setup.volume_simple,                       "volume_simple"
10427   },
10428   {
10429     TYPE_INTEGER,
10430     &setup.volume_loops,                        "volume_loops"
10431   },
10432   {
10433     TYPE_INTEGER,
10434     &setup.volume_music,                        "volume_music"
10435   },
10436   {
10437     TYPE_SWITCH,
10438     &setup.network_mode,                        "network_mode"
10439   },
10440   {
10441     TYPE_PLAYER,
10442     &setup.network_player_nr,                   "network_player"
10443   },
10444   {
10445     TYPE_STRING,
10446     &setup.network_server_hostname,             "network_server_hostname"
10447   },
10448   {
10449     TYPE_STRING,
10450     &setup.touch.control_type,                  "touch.control_type"
10451   },
10452   {
10453     TYPE_INTEGER,
10454     &setup.touch.move_distance,                 "touch.move_distance"
10455   },
10456   {
10457     TYPE_INTEGER,
10458     &setup.touch.drop_distance,                 "touch.drop_distance"
10459   },
10460   {
10461     TYPE_INTEGER,
10462     &setup.touch.transparency,                  "touch.transparency"
10463   },
10464   {
10465     TYPE_INTEGER,
10466     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10467   },
10468   {
10469     TYPE_INTEGER,
10470     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10471   },
10472   {
10473     TYPE_INTEGER,
10474     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10475   },
10476   {
10477     TYPE_INTEGER,
10478     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10479   },
10480   {
10481     TYPE_INTEGER,
10482     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10483   },
10484   {
10485     TYPE_INTEGER,
10486     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10487   },
10488   {
10489     TYPE_SWITCH,
10490     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10491   },
10492 };
10493
10494 static struct TokenInfo auto_setup_tokens[] =
10495 {
10496   {
10497     TYPE_INTEGER,
10498     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10499   },
10500 };
10501
10502 static struct TokenInfo server_setup_tokens[] =
10503 {
10504   {
10505     TYPE_STRING,
10506     &setup.player_uuid,                         "player_uuid"
10507   },
10508   {
10509     TYPE_INTEGER,
10510     &setup.player_version,                      "player_version"
10511   },
10512   {
10513     TYPE_SWITCH,
10514     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10515   },
10516   {
10517     TYPE_STRING,
10518     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10519   },
10520   {
10521     TYPE_STRING,
10522     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10523   },
10524   {
10525     TYPE_SWITCH,
10526     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10527   },
10528   {
10529     TYPE_SWITCH,
10530     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10531   },
10532   {
10533     TYPE_SWITCH,
10534     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10535   },
10536   {
10537     TYPE_SWITCH,
10538     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10539   },
10540   {
10541     TYPE_SWITCH,
10542     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10543   },
10544 };
10545
10546 static struct TokenInfo editor_setup_tokens[] =
10547 {
10548   {
10549     TYPE_SWITCH,
10550     &setup.editor.el_classic,                   "editor.el_classic"
10551   },
10552   {
10553     TYPE_SWITCH,
10554     &setup.editor.el_custom,                    "editor.el_custom"
10555   },
10556   {
10557     TYPE_SWITCH,
10558     &setup.editor.el_user_defined,              "editor.el_user_defined"
10559   },
10560   {
10561     TYPE_SWITCH,
10562     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10563   },
10564   {
10565     TYPE_SWITCH,
10566     &setup.editor.el_headlines,                 "editor.el_headlines"
10567   },
10568   {
10569     TYPE_SWITCH,
10570     &setup.editor.show_element_token,           "editor.show_element_token"
10571   },
10572   {
10573     TYPE_SWITCH,
10574     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10575   },
10576 };
10577
10578 static struct TokenInfo editor_cascade_setup_tokens[] =
10579 {
10580   {
10581     TYPE_SWITCH,
10582     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10583   },
10584   {
10585     TYPE_SWITCH,
10586     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10587   },
10588   {
10589     TYPE_SWITCH,
10590     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10591   },
10592   {
10593     TYPE_SWITCH,
10594     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10595   },
10596   {
10597     TYPE_SWITCH,
10598     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10599   },
10600   {
10601     TYPE_SWITCH,
10602     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10603   },
10604   {
10605     TYPE_SWITCH,
10606     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10607   },
10608   {
10609     TYPE_SWITCH,
10610     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10611   },
10612   {
10613     TYPE_SWITCH,
10614     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10615   },
10616   {
10617     TYPE_SWITCH,
10618     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10619   },
10620   {
10621     TYPE_SWITCH,
10622     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10623   },
10624   {
10625     TYPE_SWITCH,
10626     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10627   },
10628   {
10629     TYPE_SWITCH,
10630     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10631   },
10632   {
10633     TYPE_SWITCH,
10634     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10635   },
10636   {
10637     TYPE_SWITCH,
10638     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10639   },
10640   {
10641     TYPE_SWITCH,
10642     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10643   },
10644   {
10645     TYPE_SWITCH,
10646     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10647   },
10648   {
10649     TYPE_SWITCH,
10650     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10651   },
10652   {
10653     TYPE_SWITCH,
10654     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10655   },
10656   {
10657     TYPE_SWITCH,
10658     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10659   },
10660 };
10661
10662 static struct TokenInfo shortcut_setup_tokens[] =
10663 {
10664   {
10665     TYPE_KEY_X11,
10666     &setup.shortcut.save_game,                  "shortcut.save_game"
10667   },
10668   {
10669     TYPE_KEY_X11,
10670     &setup.shortcut.load_game,                  "shortcut.load_game"
10671   },
10672   {
10673     TYPE_KEY_X11,
10674     &setup.shortcut.restart_game,               "shortcut.restart_game"
10675   },
10676   {
10677     TYPE_KEY_X11,
10678     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10679   },
10680   {
10681     TYPE_KEY_X11,
10682     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10683   },
10684   {
10685     TYPE_KEY_X11,
10686     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10687   },
10688   {
10689     TYPE_KEY_X11,
10690     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10691   },
10692   {
10693     TYPE_KEY_X11,
10694     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10695   },
10696   {
10697     TYPE_KEY_X11,
10698     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10699   },
10700   {
10701     TYPE_KEY_X11,
10702     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10703   },
10704   {
10705     TYPE_KEY_X11,
10706     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10707   },
10708   {
10709     TYPE_KEY_X11,
10710     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10711   },
10712   {
10713     TYPE_KEY_X11,
10714     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10715   },
10716   {
10717     TYPE_KEY_X11,
10718     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10719   },
10720   {
10721     TYPE_KEY_X11,
10722     &setup.shortcut.tape_record,                "shortcut.tape_record"
10723   },
10724   {
10725     TYPE_KEY_X11,
10726     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10727   },
10728   {
10729     TYPE_KEY_X11,
10730     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10731   },
10732   {
10733     TYPE_KEY_X11,
10734     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10735   },
10736   {
10737     TYPE_KEY_X11,
10738     &setup.shortcut.sound_music,                "shortcut.sound_music"
10739   },
10740   {
10741     TYPE_KEY_X11,
10742     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10743   },
10744   {
10745     TYPE_KEY_X11,
10746     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10747   },
10748   {
10749     TYPE_KEY_X11,
10750     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10751   },
10752   {
10753     TYPE_KEY_X11,
10754     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10755   },
10756 };
10757
10758 static struct SetupInputInfo setup_input;
10759 static struct TokenInfo player_setup_tokens[] =
10760 {
10761   {
10762     TYPE_BOOLEAN,
10763     &setup_input.use_joystick,                  ".use_joystick"
10764   },
10765   {
10766     TYPE_STRING,
10767     &setup_input.joy.device_name,               ".joy.device_name"
10768   },
10769   {
10770     TYPE_INTEGER,
10771     &setup_input.joy.xleft,                     ".joy.xleft"
10772   },
10773   {
10774     TYPE_INTEGER,
10775     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10776   },
10777   {
10778     TYPE_INTEGER,
10779     &setup_input.joy.xright,                    ".joy.xright"
10780   },
10781   {
10782     TYPE_INTEGER,
10783     &setup_input.joy.yupper,                    ".joy.yupper"
10784   },
10785   {
10786     TYPE_INTEGER,
10787     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10788   },
10789   {
10790     TYPE_INTEGER,
10791     &setup_input.joy.ylower,                    ".joy.ylower"
10792   },
10793   {
10794     TYPE_INTEGER,
10795     &setup_input.joy.snap,                      ".joy.snap_field"
10796   },
10797   {
10798     TYPE_INTEGER,
10799     &setup_input.joy.drop,                      ".joy.place_bomb"
10800   },
10801   {
10802     TYPE_KEY_X11,
10803     &setup_input.key.left,                      ".key.move_left"
10804   },
10805   {
10806     TYPE_KEY_X11,
10807     &setup_input.key.right,                     ".key.move_right"
10808   },
10809   {
10810     TYPE_KEY_X11,
10811     &setup_input.key.up,                        ".key.move_up"
10812   },
10813   {
10814     TYPE_KEY_X11,
10815     &setup_input.key.down,                      ".key.move_down"
10816   },
10817   {
10818     TYPE_KEY_X11,
10819     &setup_input.key.snap,                      ".key.snap_field"
10820   },
10821   {
10822     TYPE_KEY_X11,
10823     &setup_input.key.drop,                      ".key.place_bomb"
10824   },
10825 };
10826
10827 static struct TokenInfo system_setup_tokens[] =
10828 {
10829   {
10830     TYPE_STRING,
10831     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10832   },
10833   {
10834     TYPE_STRING,
10835     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10836   },
10837   {
10838     TYPE_STRING,
10839     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10840   },
10841   {
10842     TYPE_INTEGER,
10843     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10844   },
10845 };
10846
10847 static struct TokenInfo internal_setup_tokens[] =
10848 {
10849   {
10850     TYPE_STRING,
10851     &setup.internal.program_title,              "program_title"
10852   },
10853   {
10854     TYPE_STRING,
10855     &setup.internal.program_version,            "program_version"
10856   },
10857   {
10858     TYPE_STRING,
10859     &setup.internal.program_author,             "program_author"
10860   },
10861   {
10862     TYPE_STRING,
10863     &setup.internal.program_email,              "program_email"
10864   },
10865   {
10866     TYPE_STRING,
10867     &setup.internal.program_website,            "program_website"
10868   },
10869   {
10870     TYPE_STRING,
10871     &setup.internal.program_copyright,          "program_copyright"
10872   },
10873   {
10874     TYPE_STRING,
10875     &setup.internal.program_company,            "program_company"
10876   },
10877   {
10878     TYPE_STRING,
10879     &setup.internal.program_icon_file,          "program_icon_file"
10880   },
10881   {
10882     TYPE_STRING,
10883     &setup.internal.default_graphics_set,       "default_graphics_set"
10884   },
10885   {
10886     TYPE_STRING,
10887     &setup.internal.default_sounds_set,         "default_sounds_set"
10888   },
10889   {
10890     TYPE_STRING,
10891     &setup.internal.default_music_set,          "default_music_set"
10892   },
10893   {
10894     TYPE_STRING,
10895     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10896   },
10897   {
10898     TYPE_STRING,
10899     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10900   },
10901   {
10902     TYPE_STRING,
10903     &setup.internal.fallback_music_file,        "fallback_music_file"
10904   },
10905   {
10906     TYPE_STRING,
10907     &setup.internal.default_level_series,       "default_level_series"
10908   },
10909   {
10910     TYPE_INTEGER,
10911     &setup.internal.default_window_width,       "default_window_width"
10912   },
10913   {
10914     TYPE_INTEGER,
10915     &setup.internal.default_window_height,      "default_window_height"
10916   },
10917   {
10918     TYPE_BOOLEAN,
10919     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10920   },
10921   {
10922     TYPE_BOOLEAN,
10923     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10924   },
10925   {
10926     TYPE_BOOLEAN,
10927     &setup.internal.create_user_levelset,       "create_user_levelset"
10928   },
10929   {
10930     TYPE_BOOLEAN,
10931     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10932   },
10933   {
10934     TYPE_BOOLEAN,
10935     &setup.internal.menu_game,                  "menu_game"
10936   },
10937   {
10938     TYPE_BOOLEAN,
10939     &setup.internal.menu_engines,               "menu_engines"
10940   },
10941   {
10942     TYPE_BOOLEAN,
10943     &setup.internal.menu_editor,                "menu_editor"
10944   },
10945   {
10946     TYPE_BOOLEAN,
10947     &setup.internal.menu_graphics,              "menu_graphics"
10948   },
10949   {
10950     TYPE_BOOLEAN,
10951     &setup.internal.menu_sound,                 "menu_sound"
10952   },
10953   {
10954     TYPE_BOOLEAN,
10955     &setup.internal.menu_artwork,               "menu_artwork"
10956   },
10957   {
10958     TYPE_BOOLEAN,
10959     &setup.internal.menu_input,                 "menu_input"
10960   },
10961   {
10962     TYPE_BOOLEAN,
10963     &setup.internal.menu_touch,                 "menu_touch"
10964   },
10965   {
10966     TYPE_BOOLEAN,
10967     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10968   },
10969   {
10970     TYPE_BOOLEAN,
10971     &setup.internal.menu_exit,                  "menu_exit"
10972   },
10973   {
10974     TYPE_BOOLEAN,
10975     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10976   },
10977   {
10978     TYPE_BOOLEAN,
10979     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10980   },
10981   {
10982     TYPE_BOOLEAN,
10983     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10984   },
10985   {
10986     TYPE_BOOLEAN,
10987     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10988   },
10989   {
10990     TYPE_BOOLEAN,
10991     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
10992   },
10993   {
10994     TYPE_BOOLEAN,
10995     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
10996   },
10997   {
10998     TYPE_BOOLEAN,
10999     &setup.internal.info_title,                 "info_title"
11000   },
11001   {
11002     TYPE_BOOLEAN,
11003     &setup.internal.info_elements,              "info_elements"
11004   },
11005   {
11006     TYPE_BOOLEAN,
11007     &setup.internal.info_music,                 "info_music"
11008   },
11009   {
11010     TYPE_BOOLEAN,
11011     &setup.internal.info_credits,               "info_credits"
11012   },
11013   {
11014     TYPE_BOOLEAN,
11015     &setup.internal.info_program,               "info_program"
11016   },
11017   {
11018     TYPE_BOOLEAN,
11019     &setup.internal.info_version,               "info_version"
11020   },
11021   {
11022     TYPE_BOOLEAN,
11023     &setup.internal.info_levelset,              "info_levelset"
11024   },
11025   {
11026     TYPE_BOOLEAN,
11027     &setup.internal.info_exit,                  "info_exit"
11028   },
11029 };
11030
11031 static struct TokenInfo debug_setup_tokens[] =
11032 {
11033   {
11034     TYPE_INTEGER,
11035     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11036   },
11037   {
11038     TYPE_INTEGER,
11039     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11040   },
11041   {
11042     TYPE_INTEGER,
11043     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11044   },
11045   {
11046     TYPE_INTEGER,
11047     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11048   },
11049   {
11050     TYPE_INTEGER,
11051     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11052   },
11053   {
11054     TYPE_INTEGER,
11055     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11056   },
11057   {
11058     TYPE_INTEGER,
11059     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11060   },
11061   {
11062     TYPE_INTEGER,
11063     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11064   },
11065   {
11066     TYPE_INTEGER,
11067     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11068   },
11069   {
11070     TYPE_INTEGER,
11071     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11072   },
11073   {
11074     TYPE_KEY_X11,
11075     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11076   },
11077   {
11078     TYPE_KEY_X11,
11079     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11080   },
11081   {
11082     TYPE_KEY_X11,
11083     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11084   },
11085   {
11086     TYPE_KEY_X11,
11087     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11088   },
11089   {
11090     TYPE_KEY_X11,
11091     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11092   },
11093   {
11094     TYPE_KEY_X11,
11095     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11096   },
11097   {
11098     TYPE_KEY_X11,
11099     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11100   },
11101   {
11102     TYPE_KEY_X11,
11103     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11104   },
11105   {
11106     TYPE_KEY_X11,
11107     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11108   },
11109   {
11110     TYPE_KEY_X11,
11111     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11112   },
11113   {
11114     TYPE_BOOLEAN,
11115     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11116   {
11117     TYPE_BOOLEAN,
11118     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11119   },
11120   {
11121     TYPE_BOOLEAN,
11122     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11123   },
11124   {
11125     TYPE_SWITCH3,
11126     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11127   },
11128   {
11129     TYPE_INTEGER,
11130     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11131   },
11132 };
11133
11134 static struct TokenInfo options_setup_tokens[] =
11135 {
11136   {
11137     TYPE_BOOLEAN,
11138     &setup.options.verbose,                     "options.verbose"
11139   },
11140   {
11141     TYPE_BOOLEAN,
11142     &setup.options.debug,                       "options.debug"
11143   },
11144   {
11145     TYPE_STRING,
11146     &setup.options.debug_mode,                  "options.debug_mode"
11147   },
11148 };
11149
11150 static void setSetupInfoToDefaults(struct SetupInfo *si)
11151 {
11152   int i;
11153
11154   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11155
11156   si->multiple_users = TRUE;
11157
11158   si->sound = TRUE;
11159   si->sound_loops = TRUE;
11160   si->sound_music = TRUE;
11161   si->sound_simple = TRUE;
11162   si->toons = TRUE;
11163   si->global_animations = TRUE;
11164   si->scroll_delay = TRUE;
11165   si->forced_scroll_delay = FALSE;
11166   si->scroll_delay_value = STD_SCROLL_DELAY;
11167   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11168   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11169   si->fade_screens = TRUE;
11170   si->autorecord = TRUE;
11171   si->autorecord_after_replay = TRUE;
11172   si->auto_pause_on_start = FALSE;
11173   si->show_titlescreen = TRUE;
11174   si->quick_doors = FALSE;
11175   si->team_mode = FALSE;
11176   si->handicap = TRUE;
11177   si->skip_levels = TRUE;
11178   si->increment_levels = TRUE;
11179   si->auto_play_next_level = TRUE;
11180   si->count_score_after_game = TRUE;
11181   si->show_scores_after_game = TRUE;
11182   si->time_limit = TRUE;
11183   si->fullscreen = FALSE;
11184   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11185   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11186   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11187   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11188   si->ask_on_escape = TRUE;
11189   si->ask_on_escape_editor = TRUE;
11190   si->ask_on_game_over = TRUE;
11191   si->ask_on_quit_game = TRUE;
11192   si->ask_on_quit_program = TRUE;
11193   si->quick_switch = FALSE;
11194   si->input_on_focus = FALSE;
11195   si->prefer_aga_graphics = TRUE;
11196   si->prefer_lowpass_sounds = FALSE;
11197   si->prefer_extra_panel_items = TRUE;
11198   si->game_speed_extended = FALSE;
11199   si->game_frame_delay = GAME_FRAME_DELAY;
11200   si->bd_skip_uncovering = FALSE;
11201   si->bd_skip_hatching = FALSE;
11202   si->bd_scroll_delay = TRUE;
11203   si->bd_smooth_movements = AUTO;
11204   si->sp_show_border_elements = FALSE;
11205   si->small_game_graphics = FALSE;
11206   si->show_load_save_buttons = FALSE;
11207   si->show_undo_redo_buttons = FALSE;
11208   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11209
11210   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11211   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11212   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11213
11214   si->override_level_graphics = FALSE;
11215   si->override_level_sounds = FALSE;
11216   si->override_level_music = FALSE;
11217
11218   si->volume_simple = 100;              // percent
11219   si->volume_loops = 100;               // percent
11220   si->volume_music = 100;               // percent
11221
11222   si->network_mode = FALSE;
11223   si->network_player_nr = 0;            // first player
11224   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11225
11226   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11227   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11228   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11229   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11230   si->touch.draw_outlined = TRUE;
11231   si->touch.draw_pressed = TRUE;
11232
11233   for (i = 0; i < 2; i++)
11234   {
11235     char *default_grid_button[6][2] =
11236     {
11237       { "      ", "  ^^  " },
11238       { "      ", "  ^^  " },
11239       { "      ", "<<  >>" },
11240       { "      ", "<<  >>" },
11241       { "111222", "  vv  " },
11242       { "111222", "  vv  " }
11243     };
11244     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11245     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11246     int min_xsize = MIN(6, grid_xsize);
11247     int min_ysize = MIN(6, grid_ysize);
11248     int startx = grid_xsize - min_xsize;
11249     int starty = grid_ysize - min_ysize;
11250     int x, y;
11251
11252     // virtual buttons grid can only be set to defaults if video is initialized
11253     // (this will be repeated if virtual buttons are not loaded from setup file)
11254     if (video.initialized)
11255     {
11256       si->touch.grid_xsize[i] = grid_xsize;
11257       si->touch.grid_ysize[i] = grid_ysize;
11258     }
11259     else
11260     {
11261       si->touch.grid_xsize[i] = -1;
11262       si->touch.grid_ysize[i] = -1;
11263     }
11264
11265     for (x = 0; x < MAX_GRID_XSIZE; x++)
11266       for (y = 0; y < MAX_GRID_YSIZE; y++)
11267         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11268
11269     for (x = 0; x < min_xsize; x++)
11270       for (y = 0; y < min_ysize; y++)
11271         si->touch.grid_button[i][x][starty + y] =
11272           default_grid_button[y][0][x];
11273
11274     for (x = 0; x < min_xsize; x++)
11275       for (y = 0; y < min_ysize; y++)
11276         si->touch.grid_button[i][startx + x][starty + y] =
11277           default_grid_button[y][1][x];
11278   }
11279
11280   si->touch.grid_initialized            = video.initialized;
11281
11282   si->touch.overlay_buttons             = FALSE;
11283
11284   si->editor.el_boulderdash             = TRUE;
11285   si->editor.el_boulderdash_native      = TRUE;
11286   si->editor.el_boulderdash_effects     = TRUE;
11287   si->editor.el_emerald_mine            = TRUE;
11288   si->editor.el_emerald_mine_club       = TRUE;
11289   si->editor.el_more                    = TRUE;
11290   si->editor.el_sokoban                 = TRUE;
11291   si->editor.el_supaplex                = TRUE;
11292   si->editor.el_diamond_caves           = TRUE;
11293   si->editor.el_dx_boulderdash          = TRUE;
11294
11295   si->editor.el_mirror_magic            = TRUE;
11296   si->editor.el_deflektor               = TRUE;
11297
11298   si->editor.el_chars                   = TRUE;
11299   si->editor.el_steel_chars             = TRUE;
11300
11301   si->editor.el_classic                 = TRUE;
11302   si->editor.el_custom                  = TRUE;
11303
11304   si->editor.el_user_defined            = FALSE;
11305   si->editor.el_dynamic                 = TRUE;
11306
11307   si->editor.el_headlines               = TRUE;
11308
11309   si->editor.show_element_token         = FALSE;
11310
11311   si->editor.show_read_only_warning     = TRUE;
11312
11313   si->editor.use_template_for_new_levels = TRUE;
11314
11315   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11316   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11317   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11318   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11319   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11320
11321   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11322   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11323   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11324   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11325   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11326
11327   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11328   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11329   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11330   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11331   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11332   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11333
11334   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11335   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11336   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11337
11338   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11339   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11340   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11341   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11342
11343   for (i = 0; i < MAX_PLAYERS; i++)
11344   {
11345     si->input[i].use_joystick = FALSE;
11346     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11347     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11348     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11349     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11350     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11351     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11352     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11353     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11354     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11355     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11356     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11357     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11358     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11359     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11360     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11361   }
11362
11363   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11364   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11365   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11366   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11367
11368   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11369   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11370   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11371   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11372   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11373   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11374   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11375
11376   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11377
11378   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11379   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11380   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11381
11382   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11383   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11384   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11385
11386   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11387   si->internal.choose_from_top_leveldir = FALSE;
11388   si->internal.show_scaling_in_title = TRUE;
11389   si->internal.create_user_levelset = TRUE;
11390   si->internal.info_screens_from_main = FALSE;
11391
11392   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11393   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11394
11395   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11396   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11397   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11398   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11399   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11400   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11401   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11402   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11403   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11404   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11405
11406   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11407   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11408   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11409   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11410   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11411   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11412   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11413   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11414   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11415   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11416
11417   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11418   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11419
11420   si->debug.show_frames_per_second = FALSE;
11421
11422   si->debug.xsn_mode = AUTO;
11423   si->debug.xsn_percent = 0;
11424
11425   si->options.verbose = FALSE;
11426   si->options.debug = FALSE;
11427   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11428
11429 #if defined(PLATFORM_ANDROID)
11430   si->fullscreen = TRUE;
11431   si->touch.overlay_buttons = TRUE;
11432 #endif
11433
11434   setHideSetupEntry(&setup.debug.xsn_mode);
11435 }
11436
11437 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11438 {
11439   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11440 }
11441
11442 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11443 {
11444   si->player_uuid = NULL;       // (will be set later)
11445   si->player_version = 1;       // (will be set later)
11446
11447   si->use_api_server = TRUE;
11448   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11449   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11450   si->ask_for_uploading_tapes = TRUE;
11451   si->ask_for_remaining_tapes = FALSE;
11452   si->provide_uploading_tapes = TRUE;
11453   si->ask_for_using_api_server = TRUE;
11454   si->has_remaining_tapes = FALSE;
11455 }
11456
11457 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11458 {
11459   si->editor_cascade.el_bd              = TRUE;
11460   si->editor_cascade.el_bd_native       = TRUE;
11461   si->editor_cascade.el_bd_effects      = FALSE;
11462   si->editor_cascade.el_em              = TRUE;
11463   si->editor_cascade.el_emc             = TRUE;
11464   si->editor_cascade.el_rnd             = TRUE;
11465   si->editor_cascade.el_sb              = TRUE;
11466   si->editor_cascade.el_sp              = TRUE;
11467   si->editor_cascade.el_dc              = TRUE;
11468   si->editor_cascade.el_dx              = TRUE;
11469
11470   si->editor_cascade.el_mm              = TRUE;
11471   si->editor_cascade.el_df              = TRUE;
11472
11473   si->editor_cascade.el_chars           = FALSE;
11474   si->editor_cascade.el_steel_chars     = FALSE;
11475   si->editor_cascade.el_ce              = FALSE;
11476   si->editor_cascade.el_ge              = FALSE;
11477   si->editor_cascade.el_es              = FALSE;
11478   si->editor_cascade.el_ref             = FALSE;
11479   si->editor_cascade.el_user            = FALSE;
11480   si->editor_cascade.el_dynamic         = FALSE;
11481 }
11482
11483 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11484
11485 static char *getHideSetupToken(void *setup_value)
11486 {
11487   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11488
11489   if (setup_value != NULL)
11490     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11491
11492   return hide_setup_token;
11493 }
11494
11495 void setHideSetupEntry(void *setup_value)
11496 {
11497   char *hide_setup_token = getHideSetupToken(setup_value);
11498
11499   if (hide_setup_hash == NULL)
11500     hide_setup_hash = newSetupFileHash();
11501
11502   if (setup_value != NULL)
11503     setHashEntry(hide_setup_hash, hide_setup_token, "");
11504 }
11505
11506 void removeHideSetupEntry(void *setup_value)
11507 {
11508   char *hide_setup_token = getHideSetupToken(setup_value);
11509
11510   if (setup_value != NULL)
11511     removeHashEntry(hide_setup_hash, hide_setup_token);
11512 }
11513
11514 boolean hideSetupEntry(void *setup_value)
11515 {
11516   char *hide_setup_token = getHideSetupToken(setup_value);
11517
11518   return (setup_value != NULL &&
11519           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11520 }
11521
11522 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11523                                       struct TokenInfo *token_info,
11524                                       int token_nr, char *token_text)
11525 {
11526   char *token_hide_text = getStringCat2(token_text, ".hide");
11527   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11528
11529   // set the value of this setup option in the setup option structure
11530   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11531
11532   // check if this setup option should be hidden in the setup menu
11533   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11534     setHideSetupEntry(token_info[token_nr].value);
11535
11536   free(token_hide_text);
11537 }
11538
11539 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11540                                       struct TokenInfo *token_info,
11541                                       int token_nr)
11542 {
11543   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11544                             token_info[token_nr].text);
11545 }
11546
11547 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11548 {
11549   int i, pnr;
11550
11551   if (!setup_file_hash)
11552     return;
11553
11554   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11555     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11556
11557   setup.touch.grid_initialized = TRUE;
11558   for (i = 0; i < 2; i++)
11559   {
11560     int grid_xsize = setup.touch.grid_xsize[i];
11561     int grid_ysize = setup.touch.grid_ysize[i];
11562     int x, y;
11563
11564     // if virtual buttons are not loaded from setup file, repeat initializing
11565     // virtual buttons grid with default values later when video is initialized
11566     if (grid_xsize == -1 ||
11567         grid_ysize == -1)
11568     {
11569       setup.touch.grid_initialized = FALSE;
11570
11571       continue;
11572     }
11573
11574     for (y = 0; y < grid_ysize; y++)
11575     {
11576       char token_string[MAX_LINE_LEN];
11577
11578       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11579
11580       char *value_string = getHashEntry(setup_file_hash, token_string);
11581
11582       if (value_string == NULL)
11583         continue;
11584
11585       for (x = 0; x < grid_xsize; x++)
11586       {
11587         char c = value_string[x];
11588
11589         setup.touch.grid_button[i][x][y] =
11590           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11591       }
11592     }
11593   }
11594
11595   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11596     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11597
11598   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11599     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11600
11601   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11602   {
11603     char prefix[30];
11604
11605     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11606
11607     setup_input = setup.input[pnr];
11608     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11609     {
11610       char full_token[100];
11611
11612       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11613       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11614                                 full_token);
11615     }
11616     setup.input[pnr] = setup_input;
11617   }
11618
11619   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11620     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11621
11622   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11623     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11624
11625   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11626     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11627
11628   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11629     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11630
11631   setHideRelatedSetupEntries();
11632 }
11633
11634 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11635 {
11636   int i;
11637
11638   if (!setup_file_hash)
11639     return;
11640
11641   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11642     setSetupInfo(auto_setup_tokens, i,
11643                  getHashEntry(setup_file_hash,
11644                               auto_setup_tokens[i].text));
11645 }
11646
11647 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11648 {
11649   int i;
11650
11651   if (!setup_file_hash)
11652     return;
11653
11654   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11655     setSetupInfo(server_setup_tokens, i,
11656                  getHashEntry(setup_file_hash,
11657                               server_setup_tokens[i].text));
11658 }
11659
11660 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11661 {
11662   int i;
11663
11664   if (!setup_file_hash)
11665     return;
11666
11667   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11668     setSetupInfo(editor_cascade_setup_tokens, i,
11669                  getHashEntry(setup_file_hash,
11670                               editor_cascade_setup_tokens[i].text));
11671 }
11672
11673 void LoadUserNames(void)
11674 {
11675   int last_user_nr = user.nr;
11676   int i;
11677
11678   if (global.user_names != NULL)
11679   {
11680     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11681       checked_free(global.user_names[i]);
11682
11683     checked_free(global.user_names);
11684   }
11685
11686   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11687
11688   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11689   {
11690     user.nr = i;
11691
11692     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11693
11694     if (setup_file_hash)
11695     {
11696       char *player_name = getHashEntry(setup_file_hash, "player_name");
11697
11698       global.user_names[i] = getFixedUserName(player_name);
11699
11700       freeSetupFileHash(setup_file_hash);
11701     }
11702
11703     if (global.user_names[i] == NULL)
11704       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11705   }
11706
11707   user.nr = last_user_nr;
11708 }
11709
11710 void LoadSetupFromFilename(char *filename)
11711 {
11712   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11713
11714   if (setup_file_hash)
11715   {
11716     decodeSetupFileHash_Default(setup_file_hash);
11717
11718     freeSetupFileHash(setup_file_hash);
11719   }
11720   else
11721   {
11722     Debug("setup", "using default setup values");
11723   }
11724 }
11725
11726 static void LoadSetup_SpecialPostProcessing(void)
11727 {
11728   char *player_name_new;
11729
11730   // needed to work around problems with fixed length strings
11731   player_name_new = getFixedUserName(setup.player_name);
11732   free(setup.player_name);
11733   setup.player_name = player_name_new;
11734
11735   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11736   if (setup.scroll_delay == FALSE)
11737   {
11738     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11739     setup.scroll_delay = TRUE;                  // now always "on"
11740   }
11741
11742   // make sure that scroll delay value stays inside valid range
11743   setup.scroll_delay_value =
11744     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11745 }
11746
11747 void LoadSetup_Default(void)
11748 {
11749   char *filename;
11750
11751   // always start with reliable default values
11752   setSetupInfoToDefaults(&setup);
11753
11754   // try to load setup values from default setup file
11755   filename = getDefaultSetupFilename();
11756
11757   if (fileExists(filename))
11758     LoadSetupFromFilename(filename);
11759
11760   // try to load setup values from platform setup file
11761   filename = getPlatformSetupFilename();
11762
11763   if (fileExists(filename))
11764     LoadSetupFromFilename(filename);
11765
11766   // try to load setup values from user setup file
11767   filename = getSetupFilename();
11768
11769   LoadSetupFromFilename(filename);
11770
11771   LoadSetup_SpecialPostProcessing();
11772 }
11773
11774 void LoadSetup_AutoSetup(void)
11775 {
11776   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11777   SetupFileHash *setup_file_hash = NULL;
11778
11779   // always start with reliable default values
11780   setSetupInfoToDefaults_AutoSetup(&setup);
11781
11782   setup_file_hash = loadSetupFileHash(filename);
11783
11784   if (setup_file_hash)
11785   {
11786     decodeSetupFileHash_AutoSetup(setup_file_hash);
11787
11788     freeSetupFileHash(setup_file_hash);
11789   }
11790
11791   free(filename);
11792 }
11793
11794 void LoadSetup_ServerSetup(void)
11795 {
11796   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11797   SetupFileHash *setup_file_hash = NULL;
11798
11799   // always start with reliable default values
11800   setSetupInfoToDefaults_ServerSetup(&setup);
11801
11802   setup_file_hash = loadSetupFileHash(filename);
11803
11804   if (setup_file_hash)
11805   {
11806     decodeSetupFileHash_ServerSetup(setup_file_hash);
11807
11808     freeSetupFileHash(setup_file_hash);
11809   }
11810
11811   free(filename);
11812
11813   if (setup.player_uuid == NULL)
11814   {
11815     // player UUID does not yet exist in setup file
11816     setup.player_uuid = getStringCopy(getUUID());
11817     setup.player_version = 2;
11818
11819     SaveSetup_ServerSetup();
11820   }
11821 }
11822
11823 void LoadSetup_EditorCascade(void)
11824 {
11825   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11826   SetupFileHash *setup_file_hash = NULL;
11827
11828   // always start with reliable default values
11829   setSetupInfoToDefaults_EditorCascade(&setup);
11830
11831   setup_file_hash = loadSetupFileHash(filename);
11832
11833   if (setup_file_hash)
11834   {
11835     decodeSetupFileHash_EditorCascade(setup_file_hash);
11836
11837     freeSetupFileHash(setup_file_hash);
11838   }
11839
11840   free(filename);
11841 }
11842
11843 void LoadSetup(void)
11844 {
11845   LoadSetup_Default();
11846   LoadSetup_AutoSetup();
11847   LoadSetup_ServerSetup();
11848   LoadSetup_EditorCascade();
11849 }
11850
11851 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11852                                            char *mapping_line)
11853 {
11854   char mapping_guid[MAX_LINE_LEN];
11855   char *mapping_start, *mapping_end;
11856
11857   // get GUID from game controller mapping line: copy complete line
11858   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11859   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11860
11861   // get GUID from game controller mapping line: cut after GUID part
11862   mapping_start = strchr(mapping_guid, ',');
11863   if (mapping_start != NULL)
11864     *mapping_start = '\0';
11865
11866   // cut newline from game controller mapping line
11867   mapping_end = strchr(mapping_line, '\n');
11868   if (mapping_end != NULL)
11869     *mapping_end = '\0';
11870
11871   // add mapping entry to game controller mappings hash
11872   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11873 }
11874
11875 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11876                                                  char *filename)
11877 {
11878   FILE *file;
11879
11880   if (!(file = fopen(filename, MODE_READ)))
11881   {
11882     Warn("cannot read game controller mappings file '%s'", filename);
11883
11884     return;
11885   }
11886
11887   while (!feof(file))
11888   {
11889     char line[MAX_LINE_LEN];
11890
11891     if (!fgets(line, MAX_LINE_LEN, file))
11892       break;
11893
11894     addGameControllerMappingToHash(mappings_hash, line);
11895   }
11896
11897   fclose(file);
11898 }
11899
11900 void SaveSetup_Default(void)
11901 {
11902   char *filename = getSetupFilename();
11903   FILE *file;
11904   int i, pnr;
11905
11906   InitUserDataDirectory();
11907
11908   if (!(file = fopen(filename, MODE_WRITE)))
11909   {
11910     Warn("cannot write setup file '%s'", filename);
11911
11912     return;
11913   }
11914
11915   fprintFileHeader(file, SETUP_FILENAME);
11916
11917   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11918   {
11919     // just to make things nicer :)
11920     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11921         global_setup_tokens[i].value == &setup.sound                    ||
11922         global_setup_tokens[i].value == &setup.graphics_set             ||
11923         global_setup_tokens[i].value == &setup.volume_simple            ||
11924         global_setup_tokens[i].value == &setup.network_mode             ||
11925         global_setup_tokens[i].value == &setup.touch.control_type       ||
11926         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11927         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11928       fprintf(file, "\n");
11929
11930     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11931   }
11932
11933   for (i = 0; i < 2; i++)
11934   {
11935     int grid_xsize = setup.touch.grid_xsize[i];
11936     int grid_ysize = setup.touch.grid_ysize[i];
11937     int x, y;
11938
11939     fprintf(file, "\n");
11940
11941     for (y = 0; y < grid_ysize; y++)
11942     {
11943       char token_string[MAX_LINE_LEN];
11944       char value_string[MAX_LINE_LEN];
11945
11946       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11947
11948       for (x = 0; x < grid_xsize; x++)
11949       {
11950         char c = setup.touch.grid_button[i][x][y];
11951
11952         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11953       }
11954
11955       value_string[grid_xsize] = '\0';
11956
11957       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11958     }
11959   }
11960
11961   fprintf(file, "\n");
11962   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11963     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11964
11965   fprintf(file, "\n");
11966   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11967     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11968
11969   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11970   {
11971     char prefix[30];
11972
11973     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11974     fprintf(file, "\n");
11975
11976     setup_input = setup.input[pnr];
11977     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11978       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11979   }
11980
11981   fprintf(file, "\n");
11982   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11983     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11984
11985   // (internal setup values not saved to user setup file)
11986
11987   fprintf(file, "\n");
11988   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11989     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11990         setup.debug.xsn_mode != AUTO)
11991       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11992
11993   fprintf(file, "\n");
11994   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11995     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11996
11997   fclose(file);
11998
11999   SetFilePermissions(filename, PERMS_PRIVATE);
12000 }
12001
12002 void SaveSetup_AutoSetup(void)
12003 {
12004   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12005   FILE *file;
12006   int i;
12007
12008   InitUserDataDirectory();
12009
12010   if (!(file = fopen(filename, MODE_WRITE)))
12011   {
12012     Warn("cannot write auto setup file '%s'", filename);
12013
12014     free(filename);
12015
12016     return;
12017   }
12018
12019   fprintFileHeader(file, AUTOSETUP_FILENAME);
12020
12021   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12022     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12023
12024   fclose(file);
12025
12026   SetFilePermissions(filename, PERMS_PRIVATE);
12027
12028   free(filename);
12029 }
12030
12031 void SaveSetup_ServerSetup(void)
12032 {
12033   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12034   FILE *file;
12035   int i;
12036
12037   InitUserDataDirectory();
12038
12039   if (!(file = fopen(filename, MODE_WRITE)))
12040   {
12041     Warn("cannot write server setup file '%s'", filename);
12042
12043     free(filename);
12044
12045     return;
12046   }
12047
12048   fprintFileHeader(file, SERVERSETUP_FILENAME);
12049
12050   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12051   {
12052     // just to make things nicer :)
12053     if (server_setup_tokens[i].value == &setup.use_api_server)
12054       fprintf(file, "\n");
12055
12056     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12057   }
12058
12059   fclose(file);
12060
12061   SetFilePermissions(filename, PERMS_PRIVATE);
12062
12063   free(filename);
12064 }
12065
12066 void SaveSetup_EditorCascade(void)
12067 {
12068   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12069   FILE *file;
12070   int i;
12071
12072   InitUserDataDirectory();
12073
12074   if (!(file = fopen(filename, MODE_WRITE)))
12075   {
12076     Warn("cannot write editor cascade state file '%s'", filename);
12077
12078     free(filename);
12079
12080     return;
12081   }
12082
12083   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12084
12085   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12086     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12087
12088   fclose(file);
12089
12090   SetFilePermissions(filename, PERMS_PRIVATE);
12091
12092   free(filename);
12093 }
12094
12095 void SaveSetup(void)
12096 {
12097   SaveSetup_Default();
12098   SaveSetup_AutoSetup();
12099   SaveSetup_ServerSetup();
12100   SaveSetup_EditorCascade();
12101 }
12102
12103 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12104                                                   char *filename)
12105 {
12106   FILE *file;
12107
12108   if (!(file = fopen(filename, MODE_WRITE)))
12109   {
12110     Warn("cannot write game controller mappings file '%s'", filename);
12111
12112     return;
12113   }
12114
12115   BEGIN_HASH_ITERATION(mappings_hash, itr)
12116   {
12117     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12118   }
12119   END_HASH_ITERATION(mappings_hash, itr)
12120
12121   fclose(file);
12122 }
12123
12124 void SaveSetup_AddGameControllerMapping(char *mapping)
12125 {
12126   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12127   SetupFileHash *mappings_hash = newSetupFileHash();
12128
12129   InitUserDataDirectory();
12130
12131   // load existing personal game controller mappings
12132   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12133
12134   // add new mapping to personal game controller mappings
12135   addGameControllerMappingToHash(mappings_hash, mapping);
12136
12137   // save updated personal game controller mappings
12138   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12139
12140   freeSetupFileHash(mappings_hash);
12141   free(filename);
12142 }
12143
12144 void LoadCustomElementDescriptions(void)
12145 {
12146   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12147   SetupFileHash *setup_file_hash;
12148   int i;
12149
12150   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12151   {
12152     if (element_info[i].custom_description != NULL)
12153     {
12154       free(element_info[i].custom_description);
12155       element_info[i].custom_description = NULL;
12156     }
12157   }
12158
12159   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12160     return;
12161
12162   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12163   {
12164     char *token = getStringCat2(element_info[i].token_name, ".name");
12165     char *value = getHashEntry(setup_file_hash, token);
12166
12167     if (value != NULL)
12168       element_info[i].custom_description = getStringCopy(value);
12169
12170     free(token);
12171   }
12172
12173   freeSetupFileHash(setup_file_hash);
12174 }
12175
12176 static int getElementFromToken(char *token)
12177 {
12178   char *value = getHashEntry(element_token_hash, token);
12179
12180   if (value != NULL)
12181     return atoi(value);
12182
12183   Warn("unknown element token '%s'", token);
12184
12185   return EL_UNDEFINED;
12186 }
12187
12188 void FreeGlobalAnimEventInfo(void)
12189 {
12190   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12191
12192   if (gaei->event_list == NULL)
12193     return;
12194
12195   int i;
12196
12197   for (i = 0; i < gaei->num_event_lists; i++)
12198   {
12199     checked_free(gaei->event_list[i]->event_value);
12200     checked_free(gaei->event_list[i]);
12201   }
12202
12203   checked_free(gaei->event_list);
12204
12205   gaei->event_list = NULL;
12206   gaei->num_event_lists = 0;
12207 }
12208
12209 static int AddGlobalAnimEventList(void)
12210 {
12211   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12212   int list_pos = gaei->num_event_lists++;
12213
12214   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12215                                      sizeof(struct GlobalAnimEventListInfo *));
12216
12217   gaei->event_list[list_pos] =
12218     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12219
12220   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12221
12222   gaeli->event_value = NULL;
12223   gaeli->num_event_values = 0;
12224
12225   return list_pos;
12226 }
12227
12228 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12229 {
12230   // do not add empty global animation events
12231   if (event_value == ANIM_EVENT_NONE)
12232     return list_pos;
12233
12234   // if list position is undefined, create new list
12235   if (list_pos == ANIM_EVENT_UNDEFINED)
12236     list_pos = AddGlobalAnimEventList();
12237
12238   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12239   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12240   int value_pos = gaeli->num_event_values++;
12241
12242   gaeli->event_value = checked_realloc(gaeli->event_value,
12243                                        gaeli->num_event_values * sizeof(int *));
12244
12245   gaeli->event_value[value_pos] = event_value;
12246
12247   return list_pos;
12248 }
12249
12250 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12251 {
12252   if (list_pos == ANIM_EVENT_UNDEFINED)
12253     return ANIM_EVENT_NONE;
12254
12255   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12256   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12257
12258   return gaeli->event_value[value_pos];
12259 }
12260
12261 int GetGlobalAnimEventValueCount(int list_pos)
12262 {
12263   if (list_pos == ANIM_EVENT_UNDEFINED)
12264     return 0;
12265
12266   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12267   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12268
12269   return gaeli->num_event_values;
12270 }
12271
12272 // This function checks if a string <s> of the format "string1, string2, ..."
12273 // exactly contains a string <s_contained>.
12274
12275 static boolean string_has_parameter(char *s, char *s_contained)
12276 {
12277   char *substring;
12278
12279   if (s == NULL || s_contained == NULL)
12280     return FALSE;
12281
12282   if (strlen(s_contained) > strlen(s))
12283     return FALSE;
12284
12285   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12286   {
12287     char next_char = s[strlen(s_contained)];
12288
12289     // check if next character is delimiter or whitespace
12290     if (next_char == ',' || next_char == '\0' ||
12291         next_char == ' ' || next_char == '\t')
12292       return TRUE;
12293   }
12294
12295   // check if string contains another parameter string after a comma
12296   substring = strchr(s, ',');
12297   if (substring == NULL)        // string does not contain a comma
12298     return FALSE;
12299
12300   // advance string pointer to next character after the comma
12301   substring++;
12302
12303   // skip potential whitespaces after the comma
12304   while (*substring == ' ' || *substring == '\t')
12305     substring++;
12306
12307   return string_has_parameter(substring, s_contained);
12308 }
12309
12310 static int get_anim_parameter_value_ce(char *s)
12311 {
12312   char *s_ptr = s;
12313   char *pattern_1 = "ce_change:custom_";
12314   char *pattern_2 = ".page_";
12315   int pattern_1_len = strlen(pattern_1);
12316   char *matching_char = strstr(s_ptr, pattern_1);
12317   int result = ANIM_EVENT_NONE;
12318
12319   if (matching_char == NULL)
12320     return ANIM_EVENT_NONE;
12321
12322   result = ANIM_EVENT_CE_CHANGE;
12323
12324   s_ptr = matching_char + pattern_1_len;
12325
12326   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12327   if (*s_ptr >= '0' && *s_ptr <= '9')
12328   {
12329     int gic_ce_nr = (*s_ptr++ - '0');
12330
12331     if (*s_ptr >= '0' && *s_ptr <= '9')
12332     {
12333       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12334
12335       if (*s_ptr >= '0' && *s_ptr <= '9')
12336         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12337     }
12338
12339     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12340       return ANIM_EVENT_NONE;
12341
12342     // custom element stored as 0 to 255
12343     gic_ce_nr--;
12344
12345     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12346   }
12347   else
12348   {
12349     // invalid custom element number specified
12350
12351     return ANIM_EVENT_NONE;
12352   }
12353
12354   // check for change page number ("page_X" or "page_XX") (optional)
12355   if (strPrefix(s_ptr, pattern_2))
12356   {
12357     s_ptr += strlen(pattern_2);
12358
12359     if (*s_ptr >= '0' && *s_ptr <= '9')
12360     {
12361       int gic_page_nr = (*s_ptr++ - '0');
12362
12363       if (*s_ptr >= '0' && *s_ptr <= '9')
12364         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12365
12366       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12367         return ANIM_EVENT_NONE;
12368
12369       // change page stored as 1 to 32 (0 means "all change pages")
12370
12371       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12372     }
12373     else
12374     {
12375       // invalid animation part number specified
12376
12377       return ANIM_EVENT_NONE;
12378     }
12379   }
12380
12381   // discard result if next character is neither delimiter nor whitespace
12382   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12383         *s_ptr == ' ' || *s_ptr == '\t'))
12384     return ANIM_EVENT_NONE;
12385
12386   return result;
12387 }
12388
12389 static int get_anim_parameter_value(char *s)
12390 {
12391   int event_value[] =
12392   {
12393     ANIM_EVENT_CLICK,
12394     ANIM_EVENT_INIT,
12395     ANIM_EVENT_START,
12396     ANIM_EVENT_END,
12397     ANIM_EVENT_POST
12398   };
12399   char *pattern_1[] =
12400   {
12401     "click:anim_",
12402     "init:anim_",
12403     "start:anim_",
12404     "end:anim_",
12405     "post:anim_"
12406   };
12407   char *pattern_2 = ".part_";
12408   char *matching_char = NULL;
12409   char *s_ptr = s;
12410   int pattern_1_len = 0;
12411   int result = ANIM_EVENT_NONE;
12412   int i;
12413
12414   result = get_anim_parameter_value_ce(s);
12415
12416   if (result != ANIM_EVENT_NONE)
12417     return result;
12418
12419   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12420   {
12421     matching_char = strstr(s_ptr, pattern_1[i]);
12422     pattern_1_len = strlen(pattern_1[i]);
12423     result = event_value[i];
12424
12425     if (matching_char != NULL)
12426       break;
12427   }
12428
12429   if (matching_char == NULL)
12430     return ANIM_EVENT_NONE;
12431
12432   s_ptr = matching_char + pattern_1_len;
12433
12434   // check for main animation number ("anim_X" or "anim_XX")
12435   if (*s_ptr >= '0' && *s_ptr <= '9')
12436   {
12437     int gic_anim_nr = (*s_ptr++ - '0');
12438
12439     if (*s_ptr >= '0' && *s_ptr <= '9')
12440       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12441
12442     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12443       return ANIM_EVENT_NONE;
12444
12445     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12446   }
12447   else
12448   {
12449     // invalid main animation number specified
12450
12451     return ANIM_EVENT_NONE;
12452   }
12453
12454   // check for animation part number ("part_X" or "part_XX") (optional)
12455   if (strPrefix(s_ptr, pattern_2))
12456   {
12457     s_ptr += strlen(pattern_2);
12458
12459     if (*s_ptr >= '0' && *s_ptr <= '9')
12460     {
12461       int gic_part_nr = (*s_ptr++ - '0');
12462
12463       if (*s_ptr >= '0' && *s_ptr <= '9')
12464         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12465
12466       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12467         return ANIM_EVENT_NONE;
12468
12469       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12470     }
12471     else
12472     {
12473       // invalid animation part number specified
12474
12475       return ANIM_EVENT_NONE;
12476     }
12477   }
12478
12479   // discard result if next character is neither delimiter nor whitespace
12480   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12481         *s_ptr == ' ' || *s_ptr == '\t'))
12482     return ANIM_EVENT_NONE;
12483
12484   return result;
12485 }
12486
12487 static int get_anim_parameter_values(char *s)
12488 {
12489   int list_pos = ANIM_EVENT_UNDEFINED;
12490   int event_value = ANIM_EVENT_DEFAULT;
12491
12492   if (string_has_parameter(s, "any"))
12493     event_value |= ANIM_EVENT_ANY;
12494
12495   if (string_has_parameter(s, "click:self") ||
12496       string_has_parameter(s, "click") ||
12497       string_has_parameter(s, "self"))
12498     event_value |= ANIM_EVENT_SELF;
12499
12500   if (string_has_parameter(s, "unclick:any"))
12501     event_value |= ANIM_EVENT_UNCLICK_ANY;
12502
12503   // if animation event found, add it to global animation event list
12504   if (event_value != ANIM_EVENT_NONE)
12505     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12506
12507   while (s != NULL)
12508   {
12509     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12510     event_value = get_anim_parameter_value(s);
12511
12512     // if animation event found, add it to global animation event list
12513     if (event_value != ANIM_EVENT_NONE)
12514       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12515
12516     // continue with next part of the string, starting with next comma
12517     s = strchr(s + 1, ',');
12518   }
12519
12520   return list_pos;
12521 }
12522
12523 static int get_anim_action_parameter_value(char *token)
12524 {
12525   // check most common default case first to massively speed things up
12526   if (strEqual(token, ARG_UNDEFINED))
12527     return ANIM_EVENT_ACTION_NONE;
12528
12529   int result = getImageIDFromToken(token);
12530
12531   if (result == -1)
12532   {
12533     char *gfx_token = getStringCat2("gfx.", token);
12534
12535     result = getImageIDFromToken(gfx_token);
12536
12537     checked_free(gfx_token);
12538   }
12539
12540   if (result == -1)
12541   {
12542     Key key = getKeyFromX11KeyName(token);
12543
12544     if (key != KSYM_UNDEFINED)
12545       result = -(int)key;
12546   }
12547
12548   if (result == -1)
12549   {
12550     if (isURL(token))
12551     {
12552       result = get_hash_from_string(token);     // unsigned int => int
12553       result = ABS(result);                     // may be negative now
12554       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12555
12556       setHashEntry(anim_url_hash, int2str(result, 0), token);
12557     }
12558   }
12559
12560   if (result == -1)
12561     result = ANIM_EVENT_ACTION_NONE;
12562
12563   return result;
12564 }
12565
12566 int get_parameter_value(char *value_raw, char *suffix, int type)
12567 {
12568   char *value = getStringToLower(value_raw);
12569   int result = 0;       // probably a save default value
12570
12571   if (strEqual(suffix, ".direction"))
12572   {
12573     result = (strEqual(value, "left")  ? MV_LEFT :
12574               strEqual(value, "right") ? MV_RIGHT :
12575               strEqual(value, "up")    ? MV_UP :
12576               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12577   }
12578   else if (strEqual(suffix, ".position"))
12579   {
12580     result = (strEqual(value, "left")   ? POS_LEFT :
12581               strEqual(value, "right")  ? POS_RIGHT :
12582               strEqual(value, "top")    ? POS_TOP :
12583               strEqual(value, "upper")  ? POS_UPPER :
12584               strEqual(value, "middle") ? POS_MIDDLE :
12585               strEqual(value, "lower")  ? POS_LOWER :
12586               strEqual(value, "bottom") ? POS_BOTTOM :
12587               strEqual(value, "any")    ? POS_ANY :
12588               strEqual(value, "ce")     ? POS_CE :
12589               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12590               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12591   }
12592   else if (strEqual(suffix, ".align"))
12593   {
12594     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12595               strEqual(value, "right")  ? ALIGN_RIGHT :
12596               strEqual(value, "center") ? ALIGN_CENTER :
12597               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12598   }
12599   else if (strEqual(suffix, ".valign"))
12600   {
12601     result = (strEqual(value, "top")    ? VALIGN_TOP :
12602               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12603               strEqual(value, "middle") ? VALIGN_MIDDLE :
12604               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12605   }
12606   else if (strEqual(suffix, ".anim_mode"))
12607   {
12608     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12609               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12610               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12611               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12612               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12613               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12614               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12615               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12616               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12617               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12618               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12619               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12620               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12621               string_has_parameter(value, "all")        ? ANIM_ALL :
12622               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12623               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12624               ANIM_DEFAULT);
12625
12626     if (string_has_parameter(value, "once"))
12627       result |= ANIM_ONCE;
12628
12629     if (string_has_parameter(value, "reverse"))
12630       result |= ANIM_REVERSE;
12631
12632     if (string_has_parameter(value, "opaque_player"))
12633       result |= ANIM_OPAQUE_PLAYER;
12634
12635     if (string_has_parameter(value, "static_panel"))
12636       result |= ANIM_STATIC_PANEL;
12637   }
12638   else if (strEqual(suffix, ".init_event") ||
12639            strEqual(suffix, ".anim_event"))
12640   {
12641     result = get_anim_parameter_values(value);
12642   }
12643   else if (strEqual(suffix, ".init_delay_action") ||
12644            strEqual(suffix, ".anim_delay_action") ||
12645            strEqual(suffix, ".post_delay_action") ||
12646            strEqual(suffix, ".init_event_action") ||
12647            strEqual(suffix, ".anim_event_action"))
12648   {
12649     result = get_anim_action_parameter_value(value_raw);
12650   }
12651   else if (strEqual(suffix, ".class"))
12652   {
12653     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12654               get_hash_from_string(value));
12655   }
12656   else if (strEqual(suffix, ".style"))
12657   {
12658     result = STYLE_DEFAULT;
12659
12660     if (string_has_parameter(value, "accurate_borders"))
12661       result |= STYLE_ACCURATE_BORDERS;
12662
12663     if (string_has_parameter(value, "inner_corners"))
12664       result |= STYLE_INNER_CORNERS;
12665
12666     if (string_has_parameter(value, "reverse"))
12667       result |= STYLE_REVERSE;
12668
12669     if (string_has_parameter(value, "leftmost_position"))
12670       result |= STYLE_LEFTMOST_POSITION;
12671
12672     if (string_has_parameter(value, "block_clicks"))
12673       result |= STYLE_BLOCK;
12674
12675     if (string_has_parameter(value, "passthrough_clicks"))
12676       result |= STYLE_PASSTHROUGH;
12677
12678     if (string_has_parameter(value, "multiple_actions"))
12679       result |= STYLE_MULTIPLE_ACTIONS;
12680
12681     if (string_has_parameter(value, "consume_ce_event"))
12682       result |= STYLE_CONSUME_CE_EVENT;
12683   }
12684   else if (strEqual(suffix, ".fade_mode"))
12685   {
12686     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12687               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12688               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12689               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12690               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12691               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12692               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12693               FADE_MODE_DEFAULT);
12694   }
12695   else if (strEqual(suffix, ".auto_delay_unit"))
12696   {
12697     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12698               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12699               AUTO_DELAY_UNIT_DEFAULT);
12700   }
12701   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12702   {
12703     result = gfx.get_font_from_token_function(value);
12704   }
12705   else          // generic parameter of type integer or boolean
12706   {
12707     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12708               type == TYPE_INTEGER ? get_integer_from_string(value) :
12709               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12710               ARG_UNDEFINED_VALUE);
12711   }
12712
12713   free(value);
12714
12715   return result;
12716 }
12717
12718 static int get_token_parameter_value(char *token, char *value_raw)
12719 {
12720   char *suffix;
12721
12722   if (token == NULL || value_raw == NULL)
12723     return ARG_UNDEFINED_VALUE;
12724
12725   suffix = strrchr(token, '.');
12726   if (suffix == NULL)
12727     suffix = token;
12728
12729   if (strEqual(suffix, ".element"))
12730     return getElementFromToken(value_raw);
12731
12732   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12733   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12734 }
12735
12736 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12737                                      boolean ignore_defaults)
12738 {
12739   int i;
12740
12741   for (i = 0; image_config_vars[i].token != NULL; i++)
12742   {
12743     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12744
12745     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12746     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12747       continue;
12748
12749     if (value != NULL)
12750       *image_config_vars[i].value =
12751         get_token_parameter_value(image_config_vars[i].token, value);
12752   }
12753 }
12754
12755 void InitMenuDesignSettings_Static(void)
12756 {
12757   // always start with reliable default values from static default config
12758   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12759 }
12760
12761 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12762 {
12763   int i;
12764
12765   // the following initializes hierarchical values from static configuration
12766
12767   // special case: initialize "ARG_DEFAULT" values in static default config
12768   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12769   titlescreen_initial_first_default.fade_mode  =
12770     title_initial_first_default.fade_mode;
12771   titlescreen_initial_first_default.fade_delay =
12772     title_initial_first_default.fade_delay;
12773   titlescreen_initial_first_default.post_delay =
12774     title_initial_first_default.post_delay;
12775   titlescreen_initial_first_default.auto_delay =
12776     title_initial_first_default.auto_delay;
12777   titlescreen_initial_first_default.auto_delay_unit =
12778     title_initial_first_default.auto_delay_unit;
12779   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12780   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12781   titlescreen_first_default.post_delay = title_first_default.post_delay;
12782   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12783   titlescreen_first_default.auto_delay_unit =
12784     title_first_default.auto_delay_unit;
12785   titlemessage_initial_first_default.fade_mode  =
12786     title_initial_first_default.fade_mode;
12787   titlemessage_initial_first_default.fade_delay =
12788     title_initial_first_default.fade_delay;
12789   titlemessage_initial_first_default.post_delay =
12790     title_initial_first_default.post_delay;
12791   titlemessage_initial_first_default.auto_delay =
12792     title_initial_first_default.auto_delay;
12793   titlemessage_initial_first_default.auto_delay_unit =
12794     title_initial_first_default.auto_delay_unit;
12795   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12796   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12797   titlemessage_first_default.post_delay = title_first_default.post_delay;
12798   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12799   titlemessage_first_default.auto_delay_unit =
12800     title_first_default.auto_delay_unit;
12801
12802   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12803   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12804   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12805   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12806   titlescreen_initial_default.auto_delay_unit =
12807     title_initial_default.auto_delay_unit;
12808   titlescreen_default.fade_mode  = title_default.fade_mode;
12809   titlescreen_default.fade_delay = title_default.fade_delay;
12810   titlescreen_default.post_delay = title_default.post_delay;
12811   titlescreen_default.auto_delay = title_default.auto_delay;
12812   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12813   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12814   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12815   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12816   titlemessage_initial_default.auto_delay_unit =
12817     title_initial_default.auto_delay_unit;
12818   titlemessage_default.fade_mode  = title_default.fade_mode;
12819   titlemessage_default.fade_delay = title_default.fade_delay;
12820   titlemessage_default.post_delay = title_default.post_delay;
12821   titlemessage_default.auto_delay = title_default.auto_delay;
12822   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12823
12824   // special case: initialize "ARG_DEFAULT" values in static default config
12825   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12826   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12827   {
12828     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12829     titlescreen_first[i] = titlescreen_first_default;
12830     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12831     titlemessage_first[i] = titlemessage_first_default;
12832
12833     titlescreen_initial[i] = titlescreen_initial_default;
12834     titlescreen[i] = titlescreen_default;
12835     titlemessage_initial[i] = titlemessage_initial_default;
12836     titlemessage[i] = titlemessage_default;
12837   }
12838
12839   // special case: initialize "ARG_DEFAULT" values in static default config
12840   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12841   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12842   {
12843     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12844       continue;
12845
12846     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12847     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12848     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12849   }
12850
12851   // special case: initialize "ARG_DEFAULT" values in static default config
12852   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12853   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12854   {
12855     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12856     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12857     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12858
12859     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12860       continue;
12861
12862     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12863   }
12864 }
12865
12866 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12867 {
12868   static struct
12869   {
12870     struct XY *dst, *src;
12871   }
12872   game_buttons_xy[] =
12873   {
12874     { &game.button.save,        &game.button.stop       },
12875     { &game.button.pause2,      &game.button.pause      },
12876     { &game.button.load,        &game.button.play       },
12877     { &game.button.undo,        &game.button.stop       },
12878     { &game.button.redo,        &game.button.play       },
12879
12880     { NULL,                     NULL                    }
12881   };
12882   int i, j;
12883
12884   // special case: initialize later added SETUP list size from LEVELS value
12885   if (menu.list_size[GAME_MODE_SETUP] == -1)
12886     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12887
12888   // set default position for snapshot buttons to stop/pause/play buttons
12889   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12890     if ((*game_buttons_xy[i].dst).x == -1 &&
12891         (*game_buttons_xy[i].dst).y == -1)
12892       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12893
12894   // --------------------------------------------------------------------------
12895   // dynamic viewports (including playfield margins, borders and alignments)
12896   // --------------------------------------------------------------------------
12897
12898   // dynamic viewports currently only supported for landscape mode
12899   int display_width  = MAX(video.display_width, video.display_height);
12900   int display_height = MIN(video.display_width, video.display_height);
12901
12902   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12903   {
12904     struct RectWithBorder *vp_window    = &viewport.window[i];
12905     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12906     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12907     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12908     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12909     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12910     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12911     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12912
12913     // adjust window size if min/max width/height is specified
12914
12915     if (vp_window->min_width != -1)
12916     {
12917       int window_width = display_width;
12918
12919       // when using static window height, use aspect ratio of display
12920       if (vp_window->min_height == -1)
12921         window_width = vp_window->height * display_width / display_height;
12922
12923       vp_window->width = MAX(vp_window->min_width, window_width);
12924     }
12925
12926     if (vp_window->min_height != -1)
12927     {
12928       int window_height = display_height;
12929
12930       // when using static window width, use aspect ratio of display
12931       if (vp_window->min_width == -1)
12932         window_height = vp_window->width * display_height / display_width;
12933
12934       vp_window->height = MAX(vp_window->min_height, window_height);
12935     }
12936
12937     if (vp_window->max_width != -1)
12938       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12939
12940     if (vp_window->max_height != -1)
12941       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12942
12943     int playfield_width  = vp_window->width;
12944     int playfield_height = vp_window->height;
12945
12946     // adjust playfield size and position according to specified margins
12947
12948     playfield_width  -= vp_playfield->margin_left;
12949     playfield_width  -= vp_playfield->margin_right;
12950
12951     playfield_height -= vp_playfield->margin_top;
12952     playfield_height -= vp_playfield->margin_bottom;
12953
12954     // adjust playfield size if min/max width/height is specified
12955
12956     if (vp_playfield->min_width != -1)
12957       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12958
12959     if (vp_playfield->min_height != -1)
12960       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12961
12962     if (vp_playfield->max_width != -1)
12963       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12964
12965     if (vp_playfield->max_height != -1)
12966       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12967
12968     // adjust playfield position according to specified alignment
12969
12970     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12971       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12972     else if (vp_playfield->align == ALIGN_CENTER)
12973       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12974     else if (vp_playfield->align == ALIGN_RIGHT)
12975       vp_playfield->x += playfield_width - vp_playfield->width;
12976
12977     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12978       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12979     else if (vp_playfield->valign == VALIGN_MIDDLE)
12980       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12981     else if (vp_playfield->valign == VALIGN_BOTTOM)
12982       vp_playfield->y += playfield_height - vp_playfield->height;
12983
12984     vp_playfield->x += vp_playfield->margin_left;
12985     vp_playfield->y += vp_playfield->margin_top;
12986
12987     // adjust individual playfield borders if only default border is specified
12988
12989     if (vp_playfield->border_left == -1)
12990       vp_playfield->border_left = vp_playfield->border_size;
12991     if (vp_playfield->border_right == -1)
12992       vp_playfield->border_right = vp_playfield->border_size;
12993     if (vp_playfield->border_top == -1)
12994       vp_playfield->border_top = vp_playfield->border_size;
12995     if (vp_playfield->border_bottom == -1)
12996       vp_playfield->border_bottom = vp_playfield->border_size;
12997
12998     // set dynamic playfield borders if borders are specified as undefined
12999     // (but only if window size was dynamic and playfield size was static)
13000
13001     if (dynamic_window_width && !dynamic_playfield_width)
13002     {
13003       if (vp_playfield->border_left == -1)
13004       {
13005         vp_playfield->border_left = (vp_playfield->x -
13006                                      vp_playfield->margin_left);
13007         vp_playfield->x     -= vp_playfield->border_left;
13008         vp_playfield->width += vp_playfield->border_left;
13009       }
13010
13011       if (vp_playfield->border_right == -1)
13012       {
13013         vp_playfield->border_right = (vp_window->width -
13014                                       vp_playfield->x -
13015                                       vp_playfield->width -
13016                                       vp_playfield->margin_right);
13017         vp_playfield->width += vp_playfield->border_right;
13018       }
13019     }
13020
13021     if (dynamic_window_height && !dynamic_playfield_height)
13022     {
13023       if (vp_playfield->border_top == -1)
13024       {
13025         vp_playfield->border_top = (vp_playfield->y -
13026                                     vp_playfield->margin_top);
13027         vp_playfield->y      -= vp_playfield->border_top;
13028         vp_playfield->height += vp_playfield->border_top;
13029       }
13030
13031       if (vp_playfield->border_bottom == -1)
13032       {
13033         vp_playfield->border_bottom = (vp_window->height -
13034                                        vp_playfield->y -
13035                                        vp_playfield->height -
13036                                        vp_playfield->margin_bottom);
13037         vp_playfield->height += vp_playfield->border_bottom;
13038       }
13039     }
13040
13041     // adjust playfield size to be a multiple of a defined alignment tile size
13042
13043     int align_size = vp_playfield->align_size;
13044     int playfield_xtiles = vp_playfield->width  / align_size;
13045     int playfield_ytiles = vp_playfield->height / align_size;
13046     int playfield_width_corrected  = playfield_xtiles * align_size;
13047     int playfield_height_corrected = playfield_ytiles * align_size;
13048     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13049                                  i == GFX_SPECIAL_ARG_EDITOR);
13050
13051     if (is_playfield_mode &&
13052         dynamic_playfield_width &&
13053         vp_playfield->width != playfield_width_corrected)
13054     {
13055       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13056
13057       vp_playfield->width = playfield_width_corrected;
13058
13059       if (vp_playfield->align == ALIGN_LEFT)
13060       {
13061         vp_playfield->border_left += playfield_xdiff;
13062       }
13063       else if (vp_playfield->align == ALIGN_RIGHT)
13064       {
13065         vp_playfield->border_right += playfield_xdiff;
13066       }
13067       else if (vp_playfield->align == ALIGN_CENTER)
13068       {
13069         int border_left_diff  = playfield_xdiff / 2;
13070         int border_right_diff = playfield_xdiff - border_left_diff;
13071
13072         vp_playfield->border_left  += border_left_diff;
13073         vp_playfield->border_right += border_right_diff;
13074       }
13075     }
13076
13077     if (is_playfield_mode &&
13078         dynamic_playfield_height &&
13079         vp_playfield->height != playfield_height_corrected)
13080     {
13081       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13082
13083       vp_playfield->height = playfield_height_corrected;
13084
13085       if (vp_playfield->valign == VALIGN_TOP)
13086       {
13087         vp_playfield->border_top += playfield_ydiff;
13088       }
13089       else if (vp_playfield->align == VALIGN_BOTTOM)
13090       {
13091         vp_playfield->border_right += playfield_ydiff;
13092       }
13093       else if (vp_playfield->align == VALIGN_MIDDLE)
13094       {
13095         int border_top_diff    = playfield_ydiff / 2;
13096         int border_bottom_diff = playfield_ydiff - border_top_diff;
13097
13098         vp_playfield->border_top    += border_top_diff;
13099         vp_playfield->border_bottom += border_bottom_diff;
13100       }
13101     }
13102
13103     // adjust door positions according to specified alignment
13104
13105     for (j = 0; j < 2; j++)
13106     {
13107       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13108
13109       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13110         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13111       else if (vp_door->align == ALIGN_CENTER)
13112         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13113       else if (vp_door->align == ALIGN_RIGHT)
13114         vp_door->x += vp_window->width - vp_door->width;
13115
13116       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13117         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13118       else if (vp_door->valign == VALIGN_MIDDLE)
13119         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13120       else if (vp_door->valign == VALIGN_BOTTOM)
13121         vp_door->y += vp_window->height - vp_door->height;
13122     }
13123   }
13124 }
13125
13126 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13127 {
13128   static struct
13129   {
13130     struct XYTileSize *dst, *src;
13131     int graphic;
13132   }
13133   editor_buttons_xy[] =
13134   {
13135     {
13136       &editor.button.element_left,      &editor.palette.element_left,
13137       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13138     },
13139     {
13140       &editor.button.element_middle,    &editor.palette.element_middle,
13141       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13142     },
13143     {
13144       &editor.button.element_right,     &editor.palette.element_right,
13145       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13146     },
13147
13148     { NULL,                     NULL                    }
13149   };
13150   int i;
13151
13152   // set default position for element buttons to element graphics
13153   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13154   {
13155     if ((*editor_buttons_xy[i].dst).x == -1 &&
13156         (*editor_buttons_xy[i].dst).y == -1)
13157     {
13158       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13159
13160       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13161
13162       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13163     }
13164   }
13165
13166   // adjust editor palette rows and columns if specified to be dynamic
13167
13168   if (editor.palette.cols == -1)
13169   {
13170     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13171     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13172     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13173
13174     editor.palette.cols = (vp_width - sc_width) / bt_width;
13175
13176     if (editor.palette.x == -1)
13177     {
13178       int palette_width = editor.palette.cols * bt_width + sc_width;
13179
13180       editor.palette.x = (vp_width - palette_width) / 2;
13181     }
13182   }
13183
13184   if (editor.palette.rows == -1)
13185   {
13186     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13187     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13188     int tx_height = getFontHeight(FONT_TEXT_2);
13189
13190     editor.palette.rows = (vp_height - tx_height) / bt_height;
13191
13192     if (editor.palette.y == -1)
13193     {
13194       int palette_height = editor.palette.rows * bt_height + tx_height;
13195
13196       editor.palette.y = (vp_height - palette_height) / 2;
13197     }
13198   }
13199 }
13200
13201 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13202                                                       boolean initialize)
13203 {
13204   // special case: check if network and preview player positions are redefined,
13205   // to compare this later against the main menu level preview being redefined
13206   struct TokenIntPtrInfo menu_config_players[] =
13207   {
13208     { "main.network_players.x", &menu.main.network_players.redefined    },
13209     { "main.network_players.y", &menu.main.network_players.redefined    },
13210     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13211     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13212     { "preview.x",              &preview.redefined                      },
13213     { "preview.y",              &preview.redefined                      }
13214   };
13215   int i;
13216
13217   if (initialize)
13218   {
13219     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13220       *menu_config_players[i].value = FALSE;
13221   }
13222   else
13223   {
13224     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13225       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13226         *menu_config_players[i].value = TRUE;
13227   }
13228 }
13229
13230 static void InitMenuDesignSettings_PreviewPlayers(void)
13231 {
13232   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13233 }
13234
13235 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13236 {
13237   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13238 }
13239
13240 static void LoadMenuDesignSettingsFromFilename(char *filename)
13241 {
13242   static struct TitleFadingInfo tfi;
13243   static struct TitleMessageInfo tmi;
13244   static struct TokenInfo title_tokens[] =
13245   {
13246     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13247     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13248     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13249     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13250     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13251
13252     { -1,               NULL,                   NULL                    }
13253   };
13254   static struct TokenInfo titlemessage_tokens[] =
13255   {
13256     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13257     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13258     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13259     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13260     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13261     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13262     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13263     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13264     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13265     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13266     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13267     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13268     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13269     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13270     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13271     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13272     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13273     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13274
13275     { -1,               NULL,                   NULL                    }
13276   };
13277   static struct
13278   {
13279     struct TitleFadingInfo *info;
13280     char *text;
13281   }
13282   title_info[] =
13283   {
13284     // initialize first titles from "enter screen" definitions, if defined
13285     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13286     { &title_first_default,             "menu.enter_screen.TITLE"       },
13287
13288     // initialize title screens from "next screen" definitions, if defined
13289     { &title_initial_default,           "menu.next_screen.TITLE"        },
13290     { &title_default,                   "menu.next_screen.TITLE"        },
13291
13292     { NULL,                             NULL                            }
13293   };
13294   static struct
13295   {
13296     struct TitleMessageInfo *array;
13297     char *text;
13298   }
13299   titlemessage_arrays[] =
13300   {
13301     // initialize first titles from "enter screen" definitions, if defined
13302     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13303     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13304     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13305     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13306
13307     // initialize titles from "next screen" definitions, if defined
13308     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13309     { titlescreen,                      "menu.next_screen.TITLE"        },
13310     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13311     { titlemessage,                     "menu.next_screen.TITLE"        },
13312
13313     // overwrite titles with title definitions, if defined
13314     { titlescreen_initial_first,        "[title_initial]"               },
13315     { titlescreen_first,                "[title]"                       },
13316     { titlemessage_initial_first,       "[title_initial]"               },
13317     { titlemessage_first,               "[title]"                       },
13318
13319     { titlescreen_initial,              "[title_initial]"               },
13320     { titlescreen,                      "[title]"                       },
13321     { titlemessage_initial,             "[title_initial]"               },
13322     { titlemessage,                     "[title]"                       },
13323
13324     // overwrite titles with title screen/message definitions, if defined
13325     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13326     { titlescreen_first,                "[titlescreen]"                 },
13327     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13328     { titlemessage_first,               "[titlemessage]"                },
13329
13330     { titlescreen_initial,              "[titlescreen_initial]"         },
13331     { titlescreen,                      "[titlescreen]"                 },
13332     { titlemessage_initial,             "[titlemessage_initial]"        },
13333     { titlemessage,                     "[titlemessage]"                },
13334
13335     { NULL,                             NULL                            }
13336   };
13337   SetupFileHash *setup_file_hash;
13338   int i, j, k;
13339
13340   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13341     return;
13342
13343   // the following initializes hierarchical values from dynamic configuration
13344
13345   // special case: initialize with default values that may be overwritten
13346   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13347   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13348   {
13349     struct TokenIntPtrInfo menu_config[] =
13350     {
13351       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13352       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13353       { "menu.list_size",       &menu.list_size[i]      }
13354     };
13355
13356     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13357     {
13358       char *token = menu_config[j].token;
13359       char *value = getHashEntry(setup_file_hash, token);
13360
13361       if (value != NULL)
13362         *menu_config[j].value = get_integer_from_string(value);
13363     }
13364   }
13365
13366   // special case: initialize with default values that may be overwritten
13367   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13368   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13369   {
13370     struct TokenIntPtrInfo menu_config[] =
13371     {
13372       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13373       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13374       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13375       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13376       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13377     };
13378
13379     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13380     {
13381       char *token = menu_config[j].token;
13382       char *value = getHashEntry(setup_file_hash, token);
13383
13384       if (value != NULL)
13385         *menu_config[j].value = get_integer_from_string(value);
13386     }
13387   }
13388
13389   // special case: initialize with default values that may be overwritten
13390   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13391   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13392   {
13393     struct TokenIntPtrInfo menu_config[] =
13394     {
13395       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13396       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13397     };
13398
13399     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13400     {
13401       char *token = menu_config[j].token;
13402       char *value = getHashEntry(setup_file_hash, token);
13403
13404       if (value != NULL)
13405         *menu_config[j].value = get_integer_from_string(value);
13406     }
13407   }
13408
13409   // special case: initialize with default values that may be overwritten
13410   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13411   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13412   {
13413     struct TokenIntPtrInfo menu_config[] =
13414     {
13415       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13416       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13417       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13418       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13419       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13420       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13421       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13422       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13423       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13424       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13425     };
13426
13427     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13428     {
13429       char *token = menu_config[j].token;
13430       char *value = getHashEntry(setup_file_hash, token);
13431
13432       if (value != NULL)
13433         *menu_config[j].value = get_integer_from_string(value);
13434     }
13435   }
13436
13437   // special case: initialize with default values that may be overwritten
13438   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13439   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13440   {
13441     struct TokenIntPtrInfo menu_config[] =
13442     {
13443       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13444       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13445       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13446       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13447       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13448       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13449       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13450       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13451       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13452     };
13453
13454     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13455     {
13456       char *token = menu_config[j].token;
13457       char *value = getHashEntry(setup_file_hash, token);
13458
13459       if (value != NULL)
13460         *menu_config[j].value = get_token_parameter_value(token, value);
13461     }
13462   }
13463
13464   // special case: initialize with default values that may be overwritten
13465   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13466   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13467   {
13468     struct
13469     {
13470       char *token_prefix;
13471       struct RectWithBorder *struct_ptr;
13472     }
13473     vp_struct[] =
13474     {
13475       { "viewport.window",      &viewport.window[i]     },
13476       { "viewport.playfield",   &viewport.playfield[i]  },
13477       { "viewport.door_1",      &viewport.door_1[i]     },
13478       { "viewport.door_2",      &viewport.door_2[i]     }
13479     };
13480
13481     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13482     {
13483       struct TokenIntPtrInfo vp_config[] =
13484       {
13485         { ".x",                 &vp_struct[j].struct_ptr->x             },
13486         { ".y",                 &vp_struct[j].struct_ptr->y             },
13487         { ".width",             &vp_struct[j].struct_ptr->width         },
13488         { ".height",            &vp_struct[j].struct_ptr->height        },
13489         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13490         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13491         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13492         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13493         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13494         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13495         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13496         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13497         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13498         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13499         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13500         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13501         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13502         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13503         { ".align",             &vp_struct[j].struct_ptr->align         },
13504         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13505       };
13506
13507       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13508       {
13509         char *token = getStringCat2(vp_struct[j].token_prefix,
13510                                     vp_config[k].token);
13511         char *value = getHashEntry(setup_file_hash, token);
13512
13513         if (value != NULL)
13514           *vp_config[k].value = get_token_parameter_value(token, value);
13515
13516         free(token);
13517       }
13518     }
13519   }
13520
13521   // special case: initialize with default values that may be overwritten
13522   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13523   for (i = 0; title_info[i].info != NULL; i++)
13524   {
13525     struct TitleFadingInfo *info = title_info[i].info;
13526     char *base_token = title_info[i].text;
13527
13528     for (j = 0; title_tokens[j].type != -1; j++)
13529     {
13530       char *token = getStringCat2(base_token, title_tokens[j].text);
13531       char *value = getHashEntry(setup_file_hash, token);
13532
13533       if (value != NULL)
13534       {
13535         int parameter_value = get_token_parameter_value(token, value);
13536
13537         tfi = *info;
13538
13539         *(int *)title_tokens[j].value = (int)parameter_value;
13540
13541         *info = tfi;
13542       }
13543
13544       free(token);
13545     }
13546   }
13547
13548   // special case: initialize with default values that may be overwritten
13549   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13550   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13551   {
13552     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13553     char *base_token = titlemessage_arrays[i].text;
13554
13555     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13556     {
13557       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13558       char *value = getHashEntry(setup_file_hash, token);
13559
13560       if (value != NULL)
13561       {
13562         int parameter_value = get_token_parameter_value(token, value);
13563
13564         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13565         {
13566           tmi = array[k];
13567
13568           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13569             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13570           else
13571             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13572
13573           array[k] = tmi;
13574         }
13575       }
13576
13577       free(token);
13578     }
13579   }
13580
13581   // read (and overwrite with) values that may be specified in config file
13582   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13583
13584   // special case: check if network and preview player positions are redefined
13585   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13586
13587   freeSetupFileHash(setup_file_hash);
13588 }
13589
13590 void LoadMenuDesignSettings(void)
13591 {
13592   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13593
13594   InitMenuDesignSettings_Static();
13595   InitMenuDesignSettings_SpecialPreProcessing();
13596   InitMenuDesignSettings_PreviewPlayers();
13597
13598   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13599   {
13600     // first look for special settings configured in level series config
13601     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13602
13603     if (fileExists(filename_base))
13604       LoadMenuDesignSettingsFromFilename(filename_base);
13605   }
13606
13607   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13608
13609   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13610     LoadMenuDesignSettingsFromFilename(filename_local);
13611
13612   InitMenuDesignSettings_SpecialPostProcessing();
13613 }
13614
13615 void LoadMenuDesignSettings_AfterGraphics(void)
13616 {
13617   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13618 }
13619
13620 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13621                                 boolean ignore_defaults)
13622 {
13623   int i;
13624
13625   for (i = 0; sound_config_vars[i].token != NULL; i++)
13626   {
13627     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13628
13629     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13630     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13631       continue;
13632
13633     if (value != NULL)
13634       *sound_config_vars[i].value =
13635         get_token_parameter_value(sound_config_vars[i].token, value);
13636   }
13637 }
13638
13639 void InitSoundSettings_Static(void)
13640 {
13641   // always start with reliable default values from static default config
13642   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13643 }
13644
13645 static void LoadSoundSettingsFromFilename(char *filename)
13646 {
13647   SetupFileHash *setup_file_hash;
13648
13649   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13650     return;
13651
13652   // read (and overwrite with) values that may be specified in config file
13653   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13654
13655   freeSetupFileHash(setup_file_hash);
13656 }
13657
13658 void LoadSoundSettings(void)
13659 {
13660   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13661
13662   InitSoundSettings_Static();
13663
13664   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13665   {
13666     // first look for special settings configured in level series config
13667     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13668
13669     if (fileExists(filename_base))
13670       LoadSoundSettingsFromFilename(filename_base);
13671   }
13672
13673   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13674
13675   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13676     LoadSoundSettingsFromFilename(filename_local);
13677 }
13678
13679 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13680 {
13681   char *filename = getEditorSetupFilename();
13682   SetupFileList *setup_file_list, *list;
13683   SetupFileHash *element_hash;
13684   int num_unknown_tokens = 0;
13685   int i;
13686
13687   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13688     return;
13689
13690   element_hash = newSetupFileHash();
13691
13692   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13693     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13694
13695   // determined size may be larger than needed (due to unknown elements)
13696   *num_elements = 0;
13697   for (list = setup_file_list; list != NULL; list = list->next)
13698     (*num_elements)++;
13699
13700   // add space for up to 3 more elements for padding that may be needed
13701   *num_elements += 3;
13702
13703   // free memory for old list of elements, if needed
13704   checked_free(*elements);
13705
13706   // allocate memory for new list of elements
13707   *elements = checked_malloc(*num_elements * sizeof(int));
13708
13709   *num_elements = 0;
13710   for (list = setup_file_list; list != NULL; list = list->next)
13711   {
13712     char *value = getHashEntry(element_hash, list->token);
13713
13714     if (value == NULL)          // try to find obsolete token mapping
13715     {
13716       char *mapped_token = get_mapped_token(list->token);
13717
13718       if (mapped_token != NULL)
13719       {
13720         value = getHashEntry(element_hash, mapped_token);
13721
13722         free(mapped_token);
13723       }
13724     }
13725
13726     if (value != NULL)
13727     {
13728       (*elements)[(*num_elements)++] = atoi(value);
13729     }
13730     else
13731     {
13732       if (num_unknown_tokens == 0)
13733       {
13734         Warn("---");
13735         Warn("unknown token(s) found in config file:");
13736         Warn("- config file: '%s'", filename);
13737
13738         num_unknown_tokens++;
13739       }
13740
13741       Warn("- token: '%s'", list->token);
13742     }
13743   }
13744
13745   if (num_unknown_tokens > 0)
13746     Warn("---");
13747
13748   while (*num_elements % 4)     // pad with empty elements, if needed
13749     (*elements)[(*num_elements)++] = EL_EMPTY;
13750
13751   freeSetupFileList(setup_file_list);
13752   freeSetupFileHash(element_hash);
13753
13754 #if 0
13755   for (i = 0; i < *num_elements; i++)
13756     Debug("editor", "element '%s' [%d]\n",
13757           element_info[(*elements)[i]].token_name, (*elements)[i]);
13758 #endif
13759 }
13760
13761 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13762                                                      boolean is_sound)
13763 {
13764   SetupFileHash *setup_file_hash = NULL;
13765   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13766   char *filename_music, *filename_prefix, *filename_info;
13767   struct
13768   {
13769     char *token;
13770     char **value_ptr;
13771   }
13772   token_to_value_ptr[] =
13773   {
13774     { "title_header",   &tmp_music_file_info.title_header       },
13775     { "artist_header",  &tmp_music_file_info.artist_header      },
13776     { "album_header",   &tmp_music_file_info.album_header       },
13777     { "year_header",    &tmp_music_file_info.year_header        },
13778     { "played_header",  &tmp_music_file_info.played_header      },
13779
13780     { "title",          &tmp_music_file_info.title              },
13781     { "artist",         &tmp_music_file_info.artist             },
13782     { "album",          &tmp_music_file_info.album              },
13783     { "year",           &tmp_music_file_info.year               },
13784     { "played",         &tmp_music_file_info.played             },
13785
13786     { NULL,             NULL                                    },
13787   };
13788   int i;
13789
13790   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13791                     getCustomMusicFilename(basename));
13792
13793   if (filename_music == NULL)
13794     return NULL;
13795
13796   // ---------- try to replace file extension ----------
13797
13798   filename_prefix = getStringCopy(filename_music);
13799   if (strrchr(filename_prefix, '.') != NULL)
13800     *strrchr(filename_prefix, '.') = '\0';
13801   filename_info = getStringCat2(filename_prefix, ".txt");
13802
13803   if (fileExists(filename_info))
13804     setup_file_hash = loadSetupFileHash(filename_info);
13805
13806   free(filename_prefix);
13807   free(filename_info);
13808
13809   if (setup_file_hash == NULL)
13810   {
13811     // ---------- try to add file extension ----------
13812
13813     filename_prefix = getStringCopy(filename_music);
13814     filename_info = getStringCat2(filename_prefix, ".txt");
13815
13816     if (fileExists(filename_info))
13817       setup_file_hash = loadSetupFileHash(filename_info);
13818
13819     free(filename_prefix);
13820     free(filename_info);
13821   }
13822
13823   if (setup_file_hash == NULL)
13824     return NULL;
13825
13826   // ---------- music file info found ----------
13827
13828   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13829
13830   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13831   {
13832     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13833
13834     *token_to_value_ptr[i].value_ptr =
13835       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13836   }
13837
13838   tmp_music_file_info.basename = getStringCopy(basename);
13839   tmp_music_file_info.music = music;
13840   tmp_music_file_info.is_sound = is_sound;
13841
13842   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13843   *new_music_file_info = tmp_music_file_info;
13844
13845   return new_music_file_info;
13846 }
13847
13848 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13849 {
13850   return get_music_file_info_ext(basename, music, FALSE);
13851 }
13852
13853 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13854 {
13855   return get_music_file_info_ext(basename, sound, TRUE);
13856 }
13857
13858 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13859                                      char *basename, boolean is_sound)
13860 {
13861   for (; list != NULL; list = list->next)
13862     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13863       return TRUE;
13864
13865   return FALSE;
13866 }
13867
13868 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13869 {
13870   return music_info_listed_ext(list, basename, FALSE);
13871 }
13872
13873 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13874 {
13875   return music_info_listed_ext(list, basename, TRUE);
13876 }
13877
13878 void LoadMusicInfo(void)
13879 {
13880   int num_music_noconf = getMusicListSize_NoConf();
13881   int num_music = getMusicListSize();
13882   int num_sounds = getSoundListSize();
13883   struct FileInfo *music, *sound;
13884   struct MusicFileInfo *next, **new;
13885
13886   int i;
13887
13888   while (music_file_info != NULL)
13889   {
13890     next = music_file_info->next;
13891
13892     checked_free(music_file_info->basename);
13893
13894     checked_free(music_file_info->title_header);
13895     checked_free(music_file_info->artist_header);
13896     checked_free(music_file_info->album_header);
13897     checked_free(music_file_info->year_header);
13898     checked_free(music_file_info->played_header);
13899
13900     checked_free(music_file_info->title);
13901     checked_free(music_file_info->artist);
13902     checked_free(music_file_info->album);
13903     checked_free(music_file_info->year);
13904     checked_free(music_file_info->played);
13905
13906     free(music_file_info);
13907
13908     music_file_info = next;
13909   }
13910
13911   new = &music_file_info;
13912
13913   // get (configured or unconfigured) music file info for all levels
13914   for (i = leveldir_current->first_level;
13915        i <= leveldir_current->last_level; i++)
13916   {
13917     int music_nr;
13918
13919     if (levelset.music[i] != MUS_UNDEFINED)
13920     {
13921       // get music file info for configured level music
13922       music_nr = levelset.music[i];
13923     }
13924     else if (num_music_noconf > 0)
13925     {
13926       // get music file info for unconfigured level music
13927       int level_pos = i - leveldir_current->first_level;
13928
13929       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13930     }
13931     else
13932     {
13933       continue;
13934     }
13935
13936     char *basename = getMusicInfoEntryFilename(music_nr);
13937
13938     if (basename == NULL)
13939       continue;
13940
13941     if (!music_info_listed(music_file_info, basename))
13942     {
13943       *new = get_music_file_info(basename, music_nr);
13944
13945       if (*new != NULL)
13946         new = &(*new)->next;
13947     }
13948   }
13949
13950   // get music file info for all remaining configured music files
13951   for (i = 0; i < num_music; i++)
13952   {
13953     music = getMusicListEntry(i);
13954
13955     if (music->filename == NULL)
13956       continue;
13957
13958     if (strEqual(music->filename, UNDEFINED_FILENAME))
13959       continue;
13960
13961     // a configured file may be not recognized as music
13962     if (!FileIsMusic(music->filename))
13963       continue;
13964
13965     if (!music_info_listed(music_file_info, music->filename))
13966     {
13967       *new = get_music_file_info(music->filename, i);
13968
13969       if (*new != NULL)
13970         new = &(*new)->next;
13971     }
13972   }
13973
13974   // get sound file info for all configured sound files
13975   for (i = 0; i < num_sounds; i++)
13976   {
13977     sound = getSoundListEntry(i);
13978
13979     if (sound->filename == NULL)
13980       continue;
13981
13982     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13983       continue;
13984
13985     // a configured file may be not recognized as sound
13986     if (!FileIsSound(sound->filename))
13987       continue;
13988
13989     if (!sound_info_listed(music_file_info, sound->filename))
13990     {
13991       *new = get_sound_file_info(sound->filename, i);
13992       if (*new != NULL)
13993         new = &(*new)->next;
13994     }
13995   }
13996
13997   // add pointers to previous list nodes
13998
13999   struct MusicFileInfo *node = music_file_info;
14000
14001   while (node != NULL)
14002   {
14003     if (node->next)
14004       node->next->prev = node;
14005
14006     node = node->next;
14007   }
14008 }
14009
14010 static void add_helpanim_entry(int element, int action, int direction,
14011                                int delay, int *num_list_entries)
14012 {
14013   struct HelpAnimInfo *new_list_entry;
14014   (*num_list_entries)++;
14015
14016   helpanim_info =
14017     checked_realloc(helpanim_info,
14018                     *num_list_entries * sizeof(struct HelpAnimInfo));
14019   new_list_entry = &helpanim_info[*num_list_entries - 1];
14020
14021   new_list_entry->element = element;
14022   new_list_entry->action = action;
14023   new_list_entry->direction = direction;
14024   new_list_entry->delay = delay;
14025 }
14026
14027 static void print_unknown_token(char *filename, char *token, int token_nr)
14028 {
14029   if (token_nr == 0)
14030   {
14031     Warn("---");
14032     Warn("unknown token(s) found in config file:");
14033     Warn("- config file: '%s'", filename);
14034   }
14035
14036   Warn("- token: '%s'", token);
14037 }
14038
14039 static void print_unknown_token_end(int token_nr)
14040 {
14041   if (token_nr > 0)
14042     Warn("---");
14043 }
14044
14045 void LoadHelpAnimInfo(void)
14046 {
14047   char *filename = getHelpAnimFilename();
14048   SetupFileList *setup_file_list = NULL, *list;
14049   SetupFileHash *element_hash, *action_hash, *direction_hash;
14050   int num_list_entries = 0;
14051   int num_unknown_tokens = 0;
14052   int i;
14053
14054   if (fileExists(filename))
14055     setup_file_list = loadSetupFileList(filename);
14056
14057   if (setup_file_list == NULL)
14058   {
14059     // use reliable default values from static configuration
14060     SetupFileList *insert_ptr;
14061
14062     insert_ptr = setup_file_list =
14063       newSetupFileList(helpanim_config[0].token,
14064                        helpanim_config[0].value);
14065
14066     for (i = 1; helpanim_config[i].token; i++)
14067       insert_ptr = addListEntry(insert_ptr,
14068                                 helpanim_config[i].token,
14069                                 helpanim_config[i].value);
14070   }
14071
14072   element_hash   = newSetupFileHash();
14073   action_hash    = newSetupFileHash();
14074   direction_hash = newSetupFileHash();
14075
14076   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14077     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14078
14079   for (i = 0; i < NUM_ACTIONS; i++)
14080     setHashEntry(action_hash, element_action_info[i].suffix,
14081                  i_to_a(element_action_info[i].value));
14082
14083   // do not store direction index (bit) here, but direction value!
14084   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14085     setHashEntry(direction_hash, element_direction_info[i].suffix,
14086                  i_to_a(1 << element_direction_info[i].value));
14087
14088   for (list = setup_file_list; list != NULL; list = list->next)
14089   {
14090     char *element_token, *action_token, *direction_token;
14091     char *element_value, *action_value, *direction_value;
14092     int delay = atoi(list->value);
14093
14094     if (strEqual(list->token, "end"))
14095     {
14096       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14097
14098       continue;
14099     }
14100
14101     /* first try to break element into element/action/direction parts;
14102        if this does not work, also accept combined "element[.act][.dir]"
14103        elements (like "dynamite.active"), which are unique elements */
14104
14105     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14106     {
14107       element_value = getHashEntry(element_hash, list->token);
14108       if (element_value != NULL)        // element found
14109         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14110                            &num_list_entries);
14111       else
14112       {
14113         // no further suffixes found -- this is not an element
14114         print_unknown_token(filename, list->token, num_unknown_tokens++);
14115       }
14116
14117       continue;
14118     }
14119
14120     // token has format "<prefix>.<something>"
14121
14122     action_token = strchr(list->token, '.');    // suffix may be action ...
14123     direction_token = action_token;             // ... or direction
14124
14125     element_token = getStringCopy(list->token);
14126     *strchr(element_token, '.') = '\0';
14127
14128     element_value = getHashEntry(element_hash, element_token);
14129
14130     if (element_value == NULL)          // this is no element
14131     {
14132       element_value = getHashEntry(element_hash, list->token);
14133       if (element_value != NULL)        // combined element found
14134         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14135                            &num_list_entries);
14136       else
14137         print_unknown_token(filename, list->token, num_unknown_tokens++);
14138
14139       free(element_token);
14140
14141       continue;
14142     }
14143
14144     action_value = getHashEntry(action_hash, action_token);
14145
14146     if (action_value != NULL)           // action found
14147     {
14148       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14149                     &num_list_entries);
14150
14151       free(element_token);
14152
14153       continue;
14154     }
14155
14156     direction_value = getHashEntry(direction_hash, direction_token);
14157
14158     if (direction_value != NULL)        // direction found
14159     {
14160       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14161                          &num_list_entries);
14162
14163       free(element_token);
14164
14165       continue;
14166     }
14167
14168     if (strchr(action_token + 1, '.') == NULL)
14169     {
14170       // no further suffixes found -- this is not an action nor direction
14171
14172       element_value = getHashEntry(element_hash, list->token);
14173       if (element_value != NULL)        // combined element found
14174         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14175                            &num_list_entries);
14176       else
14177         print_unknown_token(filename, list->token, num_unknown_tokens++);
14178
14179       free(element_token);
14180
14181       continue;
14182     }
14183
14184     // token has format "<prefix>.<suffix>.<something>"
14185
14186     direction_token = strchr(action_token + 1, '.');
14187
14188     action_token = getStringCopy(action_token);
14189     *strchr(action_token + 1, '.') = '\0';
14190
14191     action_value = getHashEntry(action_hash, action_token);
14192
14193     if (action_value == NULL)           // this is no action
14194     {
14195       element_value = getHashEntry(element_hash, list->token);
14196       if (element_value != NULL)        // combined element found
14197         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14198                            &num_list_entries);
14199       else
14200         print_unknown_token(filename, list->token, num_unknown_tokens++);
14201
14202       free(element_token);
14203       free(action_token);
14204
14205       continue;
14206     }
14207
14208     direction_value = getHashEntry(direction_hash, direction_token);
14209
14210     if (direction_value != NULL)        // direction found
14211     {
14212       add_helpanim_entry(atoi(element_value), atoi(action_value),
14213                          atoi(direction_value), delay, &num_list_entries);
14214
14215       free(element_token);
14216       free(action_token);
14217
14218       continue;
14219     }
14220
14221     // this is no direction
14222
14223     element_value = getHashEntry(element_hash, list->token);
14224     if (element_value != NULL)          // combined element found
14225       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14226                          &num_list_entries);
14227     else
14228       print_unknown_token(filename, list->token, num_unknown_tokens++);
14229
14230     free(element_token);
14231     free(action_token);
14232   }
14233
14234   print_unknown_token_end(num_unknown_tokens);
14235
14236   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14237   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14238
14239   freeSetupFileList(setup_file_list);
14240   freeSetupFileHash(element_hash);
14241   freeSetupFileHash(action_hash);
14242   freeSetupFileHash(direction_hash);
14243
14244 #if 0
14245   for (i = 0; i < num_list_entries; i++)
14246     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14247           EL_NAME(helpanim_info[i].element),
14248           helpanim_info[i].element,
14249           helpanim_info[i].action,
14250           helpanim_info[i].direction,
14251           helpanim_info[i].delay);
14252 #endif
14253 }
14254
14255 void LoadHelpTextInfo(void)
14256 {
14257   char *filename = getHelpTextFilename();
14258   int i;
14259
14260   if (helptext_info != NULL)
14261   {
14262     freeSetupFileHash(helptext_info);
14263     helptext_info = NULL;
14264   }
14265
14266   if (fileExists(filename))
14267     helptext_info = loadSetupFileHash(filename);
14268
14269   if (helptext_info == NULL)
14270   {
14271     // use reliable default values from static configuration
14272     helptext_info = newSetupFileHash();
14273
14274     for (i = 0; helptext_config[i].token; i++)
14275       setHashEntry(helptext_info,
14276                    helptext_config[i].token,
14277                    helptext_config[i].value);
14278   }
14279
14280 #if 0
14281   BEGIN_HASH_ITERATION(helptext_info, itr)
14282   {
14283     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14284           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14285   }
14286   END_HASH_ITERATION(hash, itr)
14287 #endif
14288 }
14289
14290
14291 // ----------------------------------------------------------------------------
14292 // convert levels
14293 // ----------------------------------------------------------------------------
14294
14295 #define MAX_NUM_CONVERT_LEVELS          1000
14296
14297 void ConvertLevels(void)
14298 {
14299   static LevelDirTree *convert_leveldir = NULL;
14300   static int convert_level_nr = -1;
14301   static int num_levels_handled = 0;
14302   static int num_levels_converted = 0;
14303   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14304   int i;
14305
14306   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14307                                                global.convert_leveldir);
14308
14309   if (convert_leveldir == NULL)
14310     Fail("no such level identifier: '%s'", global.convert_leveldir);
14311
14312   leveldir_current = convert_leveldir;
14313
14314   if (global.convert_level_nr != -1)
14315   {
14316     convert_leveldir->first_level = global.convert_level_nr;
14317     convert_leveldir->last_level  = global.convert_level_nr;
14318   }
14319
14320   convert_level_nr = convert_leveldir->first_level;
14321
14322   PrintLine("=", 79);
14323   Print("Converting levels\n");
14324   PrintLine("-", 79);
14325   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14326   Print("Level series name:       '%s'\n", convert_leveldir->name);
14327   Print("Level series author:     '%s'\n", convert_leveldir->author);
14328   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14329   PrintLine("=", 79);
14330   Print("\n");
14331
14332   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14333     levels_failed[i] = FALSE;
14334
14335   while (convert_level_nr <= convert_leveldir->last_level)
14336   {
14337     char *level_filename;
14338     boolean new_level;
14339
14340     level_nr = convert_level_nr++;
14341
14342     Print("Level %03d: ", level_nr);
14343
14344     LoadLevel(level_nr);
14345     if (level.no_level_file || level.no_valid_file)
14346     {
14347       Print("(no level)\n");
14348       continue;
14349     }
14350
14351     Print("converting level ... ");
14352
14353 #if 0
14354     // special case: conversion of some EMC levels as requested by ACME
14355     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14356 #endif
14357
14358     level_filename = getDefaultLevelFilename(level_nr);
14359     new_level = !fileExists(level_filename);
14360
14361     if (new_level)
14362     {
14363       SaveLevel(level_nr);
14364
14365       num_levels_converted++;
14366
14367       Print("converted.\n");
14368     }
14369     else
14370     {
14371       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14372         levels_failed[level_nr] = TRUE;
14373
14374       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14375     }
14376
14377     num_levels_handled++;
14378   }
14379
14380   Print("\n");
14381   PrintLine("=", 79);
14382   Print("Number of levels handled: %d\n", num_levels_handled);
14383   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14384          (num_levels_handled ?
14385           num_levels_converted * 100 / num_levels_handled : 0));
14386   PrintLine("-", 79);
14387   Print("Summary (for automatic parsing by scripts):\n");
14388   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14389          convert_leveldir->identifier, num_levels_converted,
14390          num_levels_handled,
14391          (num_levels_handled ?
14392           num_levels_converted * 100 / num_levels_handled : 0));
14393
14394   if (num_levels_handled != num_levels_converted)
14395   {
14396     Print(", FAILED:");
14397     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14398       if (levels_failed[i])
14399         Print(" %03d", i);
14400   }
14401
14402   Print("\n");
14403   PrintLine("=", 79);
14404
14405   CloseAllAndExit(0);
14406 }
14407
14408
14409 // ----------------------------------------------------------------------------
14410 // create and save images for use in level sketches (raw BMP format)
14411 // ----------------------------------------------------------------------------
14412
14413 void CreateLevelSketchImages(void)
14414 {
14415   Bitmap *bitmap1;
14416   Bitmap *bitmap2;
14417   int i;
14418
14419   InitElementPropertiesGfxElement();
14420
14421   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14422   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14423
14424   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14425   {
14426     int element = getMappedElement(i);
14427     char basename1[16];
14428     char basename2[16];
14429     char *filename1;
14430     char *filename2;
14431
14432     sprintf(basename1, "%04d.bmp", i);
14433     sprintf(basename2, "%04ds.bmp", i);
14434
14435     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14436     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14437
14438     DrawSizedElement(0, 0, element, TILESIZE);
14439     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14440
14441     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14442       Fail("cannot save level sketch image file '%s'", filename1);
14443
14444     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14445     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14446
14447     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14448       Fail("cannot save level sketch image file '%s'", filename2);
14449
14450     free(filename1);
14451     free(filename2);
14452
14453     // create corresponding SQL statements (for normal and small images)
14454     if (i < 1000)
14455     {
14456       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14457       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14458     }
14459
14460     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14461     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14462
14463     // optional: create content for forum level sketch demonstration post
14464     if (options.debug)
14465       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14466   }
14467
14468   FreeBitmap(bitmap1);
14469   FreeBitmap(bitmap2);
14470
14471   if (options.debug)
14472     fprintf(stderr, "\n");
14473
14474   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14475
14476   CloseAllAndExit(0);
14477 }
14478
14479
14480 // ----------------------------------------------------------------------------
14481 // create and save images for element collecting animations (raw BMP format)
14482 // ----------------------------------------------------------------------------
14483
14484 static boolean createCollectImage(int element)
14485 {
14486   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14487 }
14488
14489 void CreateCollectElementImages(void)
14490 {
14491   int i, j;
14492   int num_steps = 8;
14493   int anim_frames = num_steps - 1;
14494   int tile_size = TILESIZE;
14495   int anim_width  = tile_size * anim_frames;
14496   int anim_height = tile_size;
14497   int num_collect_images = 0;
14498   int pos_collect_images = 0;
14499
14500   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14501     if (createCollectImage(i))
14502       num_collect_images++;
14503
14504   Info("Creating %d element collecting animation images ...",
14505        num_collect_images);
14506
14507   int dst_width  = anim_width * 2;
14508   int dst_height = anim_height * num_collect_images / 2;
14509   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14510   char *basename_bmp = "RocksCollect.bmp";
14511   char *basename_png = "RocksCollect.png";
14512   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14513   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14514   int len_filename_bmp = strlen(filename_bmp);
14515   int len_filename_png = strlen(filename_png);
14516   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14517   char cmd_convert[max_command_len];
14518
14519   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14520            filename_bmp,
14521            filename_png);
14522
14523   // force using RGBA surface for destination bitmap
14524   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14525                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14526
14527   dst_bitmap->surface =
14528     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14529
14530   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14531   {
14532     if (!createCollectImage(i))
14533       continue;
14534
14535     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14536     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14537     int graphic = el2img(i);
14538     char *token_name = element_info[i].token_name;
14539     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14540     Bitmap *src_bitmap;
14541     int src_x, src_y;
14542
14543     Info("- creating collecting image for '%s' ...", token_name);
14544
14545     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14546
14547     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14548                tile_size, tile_size, 0, 0);
14549
14550     // force using RGBA surface for temporary bitmap (using transparent black)
14551     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14552                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14553
14554     tmp_bitmap->surface =
14555       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14556
14557     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14558
14559     for (j = 0; j < anim_frames; j++)
14560     {
14561       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14562       int frame_size = frame_size_final * num_steps;
14563       int offset = (tile_size - frame_size_final) / 2;
14564       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14565
14566       while (frame_size > frame_size_final)
14567       {
14568         frame_size /= 2;
14569
14570         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14571
14572         FreeBitmap(frame_bitmap);
14573
14574         frame_bitmap = half_bitmap;
14575       }
14576
14577       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14578                        frame_size_final, frame_size_final,
14579                        dst_x + j * tile_size + offset, dst_y + offset);
14580
14581       FreeBitmap(frame_bitmap);
14582     }
14583
14584     tmp_bitmap->surface_masked = NULL;
14585
14586     FreeBitmap(tmp_bitmap);
14587
14588     pos_collect_images++;
14589   }
14590
14591   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14592     Fail("cannot save element collecting image file '%s'", filename_bmp);
14593
14594   FreeBitmap(dst_bitmap);
14595
14596   Info("Converting image file from BMP to PNG ...");
14597
14598   if (system(cmd_convert) != 0)
14599     Fail("converting image file failed");
14600
14601   unlink(filename_bmp);
14602
14603   Info("Done.");
14604
14605   CloseAllAndExit(0);
14606 }
14607
14608
14609 // ----------------------------------------------------------------------------
14610 // create and save images for custom and group elements (raw BMP format)
14611 // ----------------------------------------------------------------------------
14612
14613 void CreateCustomElementImages(char *directory)
14614 {
14615   char *src_basename = "RocksCE-template.ilbm";
14616   char *dst_basename = "RocksCE.bmp";
14617   char *src_filename = getPath2(directory, src_basename);
14618   char *dst_filename = getPath2(directory, dst_basename);
14619   Bitmap *src_bitmap;
14620   Bitmap *bitmap;
14621   int yoffset_ce = 0;
14622   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14623   int i;
14624
14625   InitVideoDefaults();
14626
14627   ReCreateBitmap(&backbuffer, video.width, video.height);
14628
14629   src_bitmap = LoadImage(src_filename);
14630
14631   bitmap = CreateBitmap(TILEX * 16 * 2,
14632                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14633                         DEFAULT_DEPTH);
14634
14635   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14636   {
14637     int x = i % 16;
14638     int y = i / 16;
14639     int ii = i + 1;
14640     int j;
14641
14642     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14643                TILEX * x, TILEY * y + yoffset_ce);
14644
14645     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14646                TILEX, TILEY,
14647                TILEX * x + TILEX * 16,
14648                TILEY * y + yoffset_ce);
14649
14650     for (j = 2; j >= 0; j--)
14651     {
14652       int c = ii % 10;
14653
14654       BlitBitmap(src_bitmap, bitmap,
14655                  TILEX + c * 7, 0, 6, 10,
14656                  TILEX * x + 6 + j * 7,
14657                  TILEY * y + 11 + yoffset_ce);
14658
14659       BlitBitmap(src_bitmap, bitmap,
14660                  TILEX + c * 8, TILEY, 6, 10,
14661                  TILEX * 16 + TILEX * x + 6 + j * 8,
14662                  TILEY * y + 10 + yoffset_ce);
14663
14664       ii /= 10;
14665     }
14666   }
14667
14668   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14669   {
14670     int x = i % 16;
14671     int y = i / 16;
14672     int ii = i + 1;
14673     int j;
14674
14675     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14676                TILEX * x, TILEY * y + yoffset_ge);
14677
14678     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14679                TILEX, TILEY,
14680                TILEX * x + TILEX * 16,
14681                TILEY * y + yoffset_ge);
14682
14683     for (j = 1; j >= 0; j--)
14684     {
14685       int c = ii % 10;
14686
14687       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14688                  TILEX * x + 6 + j * 10,
14689                  TILEY * y + 11 + yoffset_ge);
14690
14691       BlitBitmap(src_bitmap, bitmap,
14692                  TILEX + c * 8, TILEY + 12, 6, 10,
14693                  TILEX * 16 + TILEX * x + 10 + j * 8,
14694                  TILEY * y + 10 + yoffset_ge);
14695
14696       ii /= 10;
14697     }
14698   }
14699
14700   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14701     Fail("cannot save CE graphics file '%s'", dst_filename);
14702
14703   FreeBitmap(bitmap);
14704
14705   CloseAllAndExit(0);
14706 }