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