added support for nut settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746
747   {
748     EL_BD_ACID,                         -1,
749     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
750     &li.bd_acid_eats_element,           EL_BD_SAND
751   },
752   {
753     EL_BD_ACID,                         -1,
754     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
755     &li.bd_acid_spread_rate,            3
756   },
757   {
758     EL_BD_ACID,                         -1,
759     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
760     &li.bd_acid_turns_to_element,       EL_EMPTY
761   },
762
763   {
764     EL_BD_BITER,                        -1,
765     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
766     &li.bd_biter_move_delay,            0
767   },
768   {
769     EL_BD_BITER,                        -1,
770     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
771     &li.bd_biter_eats_element,          EL_BD_DIAMOND
772   },
773
774   {
775     EL_BD_BLADDER,                      -1,
776     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
777     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
778   },
779
780   {
781     EL_BD_EXPANDABLE_WALL_ANY,          -1,
782     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
783     &li.bd_change_expanding_wall,       FALSE
784   },
785
786   {
787     EL_BD_REPLICATOR,                   -1,
788     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
789     &li.bd_replicators_active,          TRUE
790   },
791   {
792     EL_BD_REPLICATOR,                   -1,
793     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
794     &li.bd_replicator_create_delay,     4
795   },
796
797   {
798     EL_BD_CONVEYOR_LEFT,                -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
800     &li.bd_conveyor_belts_active,       TRUE
801   },
802   {
803     EL_BD_CONVEYOR_LEFT,                -1,
804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
805     &li.bd_conveyor_belts_changed,      FALSE
806   },
807
808   {
809     EL_BD_WATER,                        -1,
810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
811     &li.bd_water_cannot_flow_down,      FALSE
812   },
813
814   {
815     EL_BD_NUT,                          -1,
816     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
817     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
818   },
819
820   // (the following values are related to various game elements)
821
822   {
823     EL_EMERALD,                         -1,
824     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
825     &li.score[SC_EMERALD],              10
826   },
827
828   {
829     EL_DIAMOND,                         -1,
830     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
831     &li.score[SC_DIAMOND],              10
832   },
833
834   {
835     EL_BUG,                             -1,
836     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
837     &li.score[SC_BUG],                  10
838   },
839
840   {
841     EL_SPACESHIP,                       -1,
842     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
843     &li.score[SC_SPACESHIP],            10
844   },
845
846   {
847     EL_PACMAN,                          -1,
848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
849     &li.score[SC_PACMAN],               10
850   },
851
852   {
853     EL_NUT,                             -1,
854     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
855     &li.score[SC_NUT],                  10
856   },
857
858   {
859     EL_DYNAMITE,                        -1,
860     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
861     &li.score[SC_DYNAMITE],             10
862   },
863
864   {
865     EL_KEY_1,                           -1,
866     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
867     &li.score[SC_KEY],                  10
868   },
869
870   {
871     EL_PEARL,                           -1,
872     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
873     &li.score[SC_PEARL],                10
874   },
875
876   {
877     EL_CRYSTAL,                         -1,
878     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
879     &li.score[SC_CRYSTAL],              10
880   },
881
882   // (amoeba values used by R'n'D game engine only)
883   {
884     EL_BD_AMOEBA,                       -1,
885     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
886     &li.amoeba_content,                 EL_DIAMOND
887   },
888   {
889     EL_BD_AMOEBA,                       -1,
890     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
891     &li.amoeba_speed,                   10
892   },
893   {
894     EL_BD_AMOEBA,                       -1,
895     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
896     &li.grow_into_diggable,             TRUE
897   },
898   // (amoeba values used by BD game engine only)
899   {
900     EL_BD_AMOEBA,                       -1,
901     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
902     &li.bd_amoeba_wait_for_hatching,    FALSE
903   },
904   {
905     EL_BD_AMOEBA,                       -1,
906     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
907     &li.bd_amoeba_start_immediately,    TRUE
908   },
909   {
910     EL_BD_AMOEBA,                       -1,
911     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
912     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
913   },
914   {
915     EL_BD_AMOEBA,                       -1,
916     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
917     &li.bd_amoeba_threshold_too_big,    200
918   },
919   {
920     EL_BD_AMOEBA,                       -1,
921     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
922     &li.bd_amoeba_slow_growth_time,     200
923   },
924   {
925     EL_BD_AMOEBA,                       -1,
926     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
927     &li.bd_amoeba_slow_growth_rate,     3
928   },
929   {
930     EL_BD_AMOEBA,                       -1,
931     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
932     &li.bd_amoeba_fast_growth_rate,     25
933   },
934   {
935     EL_BD_AMOEBA,                       -1,
936     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
937     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
938   },
939   {
940     EL_BD_AMOEBA,                       -1,
941     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
942     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
943   },
944
945   {
946     EL_BD_AMOEBA_2,                     -1,
947     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
948     &li.bd_amoeba_2_threshold_too_big,  200
949   },
950   {
951     EL_BD_AMOEBA_2,                     -1,
952     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
953     &li.bd_amoeba_2_slow_growth_time,   200
954   },
955   {
956     EL_BD_AMOEBA_2,                     -1,
957     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
958     &li.bd_amoeba_2_slow_growth_rate,   3
959   },
960   {
961     EL_BD_AMOEBA_2,                     -1,
962     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
963     &li.bd_amoeba_2_fast_growth_rate,   25
964   },
965   {
966     EL_BD_AMOEBA_2,                     -1,
967     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
968     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
969   },
970   {
971     EL_BD_AMOEBA_2,                     -1,
972     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
973     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
974   },
975   {
976     EL_BD_AMOEBA_2,                     -1,
977     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
978     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
979   },
980   {
981     EL_BD_AMOEBA_2,                     -1,
982     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
983     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
984   },
985
986   {
987     EL_YAMYAM,                          -1,
988     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
989     &li.yamyam_content,                 EL_ROCK, NULL,
990     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
991   },
992   {
993     EL_YAMYAM,                          -1,
994     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
995     &li.score[SC_YAMYAM],               10
996   },
997
998   {
999     EL_ROBOT,                           -1,
1000     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1001     &li.score[SC_ROBOT],                10
1002   },
1003   {
1004     EL_ROBOT,                           -1,
1005     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1006     &li.slurp_score,                    10
1007   },
1008
1009   {
1010     EL_ROBOT_WHEEL,                     -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.time_wheel,                     10
1013   },
1014
1015   {
1016     EL_MAGIC_WALL,                      -1,
1017     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1018     &li.time_magic_wall,                10
1019   },
1020
1021   {
1022     EL_GAME_OF_LIFE,                    -1,
1023     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1024     &li.game_of_life[0],                2
1025   },
1026   {
1027     EL_GAME_OF_LIFE,                    -1,
1028     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1029     &li.game_of_life[1],                3
1030   },
1031   {
1032     EL_GAME_OF_LIFE,                    -1,
1033     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1034     &li.game_of_life[2],                3
1035   },
1036   {
1037     EL_GAME_OF_LIFE,                    -1,
1038     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1039     &li.game_of_life[3],                3
1040   },
1041   {
1042     EL_GAME_OF_LIFE,                    -1,
1043     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1044     &li.use_life_bugs,                  FALSE
1045   },
1046
1047   {
1048     EL_BIOMAZE,                         -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1050     &li.biomaze[0],                     2
1051   },
1052   {
1053     EL_BIOMAZE,                         -1,
1054     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1055     &li.biomaze[1],                     3
1056   },
1057   {
1058     EL_BIOMAZE,                         -1,
1059     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1060     &li.biomaze[2],                     3
1061   },
1062   {
1063     EL_BIOMAZE,                         -1,
1064     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1065     &li.biomaze[3],                     3
1066   },
1067
1068   {
1069     EL_TIMEGATE_SWITCH,                 -1,
1070     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1071     &li.time_timegate,                  10
1072   },
1073
1074   {
1075     EL_LIGHT_SWITCH_ACTIVE,             -1,
1076     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1077     &li.time_light,                     10
1078   },
1079
1080   {
1081     EL_SHIELD_NORMAL,                   -1,
1082     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1083     &li.shield_normal_time,             10
1084   },
1085   {
1086     EL_SHIELD_NORMAL,                   -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1088     &li.score[SC_SHIELD],               10
1089   },
1090
1091   {
1092     EL_SHIELD_DEADLY,                   -1,
1093     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1094     &li.shield_deadly_time,             10
1095   },
1096   {
1097     EL_SHIELD_DEADLY,                   -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1099     &li.score[SC_SHIELD],               10
1100   },
1101
1102   {
1103     EL_EXTRA_TIME,                      -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.extra_time,                     10
1106   },
1107   {
1108     EL_EXTRA_TIME,                      -1,
1109     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1110     &li.extra_time_score,               10
1111   },
1112
1113   {
1114     EL_TIME_ORB_FULL,                   -1,
1115     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1116     &li.time_orb_time,                  10
1117   },
1118   {
1119     EL_TIME_ORB_FULL,                   -1,
1120     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1121     &li.use_time_orb_bug,               FALSE
1122   },
1123
1124   {
1125     EL_SPRING,                          -1,
1126     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1127     &li.use_spring_bug,                 FALSE
1128   },
1129
1130   {
1131     EL_EMC_ANDROID,                     -1,
1132     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1133     &li.android_move_time,              10
1134   },
1135   {
1136     EL_EMC_ANDROID,                     -1,
1137     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1138     &li.android_clone_time,             10
1139   },
1140   {
1141     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1142     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1143     &li.android_clone_element[0],       EL_EMPTY, NULL,
1144     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1145   },
1146   {
1147     EL_EMC_ANDROID,                     -1,
1148     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1149     &li.android_clone_element[0],       EL_EMPTY, NULL,
1150     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1151   },
1152
1153   {
1154     EL_EMC_LENSES,                      -1,
1155     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1156     &li.lenses_score,                   10
1157   },
1158   {
1159     EL_EMC_LENSES,                      -1,
1160     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1161     &li.lenses_time,                    10
1162   },
1163
1164   {
1165     EL_EMC_MAGNIFIER,                   -1,
1166     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1167     &li.magnify_score,                  10
1168   },
1169   {
1170     EL_EMC_MAGNIFIER,                   -1,
1171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1172     &li.magnify_time,                   10
1173   },
1174
1175   {
1176     EL_EMC_MAGIC_BALL,                  -1,
1177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1178     &li.ball_time,                      10
1179   },
1180   {
1181     EL_EMC_MAGIC_BALL,                  -1,
1182     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1183     &li.ball_random,                    FALSE
1184   },
1185   {
1186     EL_EMC_MAGIC_BALL,                  -1,
1187     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1188     &li.ball_active_initial,            FALSE
1189   },
1190   {
1191     EL_EMC_MAGIC_BALL,                  -1,
1192     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1193     &li.ball_content,                   EL_EMPTY, NULL,
1194     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1195   },
1196
1197   {
1198     EL_SOKOBAN_FIELD_EMPTY,             -1,
1199     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1200     &li.sb_fields_needed,               TRUE
1201   },
1202
1203   {
1204     EL_SOKOBAN_OBJECT,                  -1,
1205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1206     &li.sb_objects_needed,              TRUE
1207   },
1208
1209   {
1210     EL_MM_MCDUFFIN,                     -1,
1211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1212     &li.mm_laser_red,                   FALSE
1213   },
1214   {
1215     EL_MM_MCDUFFIN,                     -1,
1216     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1217     &li.mm_laser_green,                 FALSE
1218   },
1219   {
1220     EL_MM_MCDUFFIN,                     -1,
1221     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1222     &li.mm_laser_blue,                  TRUE
1223   },
1224
1225   {
1226     EL_DF_LASER,                        -1,
1227     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1228     &li.df_laser_red,                   TRUE
1229   },
1230   {
1231     EL_DF_LASER,                        -1,
1232     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1233     &li.df_laser_green,                 TRUE
1234   },
1235   {
1236     EL_DF_LASER,                        -1,
1237     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1238     &li.df_laser_blue,                  FALSE
1239   },
1240
1241   {
1242     EL_MM_FUSE_ACTIVE,                  -1,
1243     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1244     &li.mm_time_fuse,                   25
1245   },
1246   {
1247     EL_MM_BOMB,                         -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.mm_time_bomb,                   75
1250   },
1251
1252   {
1253     EL_MM_GRAY_BALL,                    -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.mm_time_ball,                   75
1256   },
1257   {
1258     EL_MM_GRAY_BALL,                    -1,
1259     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1260     &li.mm_ball_choice_mode,            ANIM_RANDOM
1261   },
1262   {
1263     EL_MM_GRAY_BALL,                    -1,
1264     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1265     &li.mm_ball_content,                EL_EMPTY, NULL,
1266     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1267   },
1268   {
1269     EL_MM_GRAY_BALL,                    -1,
1270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1271     &li.rotate_mm_ball_content,         TRUE
1272   },
1273   {
1274     EL_MM_GRAY_BALL,                    -1,
1275     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1276     &li.explode_mm_ball,                FALSE
1277   },
1278
1279   {
1280     EL_MM_STEEL_BLOCK,                  -1,
1281     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1282     &li.mm_time_block,                  75
1283   },
1284   {
1285     EL_MM_LIGHTBALL,                    -1,
1286     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1287     &li.score[SC_ELEM_BONUS],           10
1288   },
1289
1290   {
1291     -1,                                 -1,
1292     -1,                                 -1,
1293     NULL,                               -1
1294   }
1295 };
1296
1297 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1298 {
1299   {
1300     -1,                                 -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1302     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1303   },
1304   {
1305     -1,                                 -1,
1306     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1307     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1308   },
1309
1310   {
1311     -1,                                 -1,
1312     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1313     &xx_envelope.autowrap,              FALSE
1314   },
1315   {
1316     -1,                                 -1,
1317     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1318     &xx_envelope.centered,              FALSE
1319   },
1320
1321   {
1322     -1,                                 -1,
1323     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1324     &xx_envelope.text,                  -1, NULL,
1325     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1326     &xx_default_string_empty[0]
1327   },
1328
1329   {
1330     -1,                                 -1,
1331     -1,                                 -1,
1332     NULL,                               -1
1333   }
1334 };
1335
1336 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1337 {
1338   {
1339     -1,                                 -1,
1340     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1341     &xx_ei.description[0],              -1,
1342     &yy_ei.description[0],
1343     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1344     &xx_default_description[0]
1345   },
1346
1347   {
1348     -1,                                 -1,
1349     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1350     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1351     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1352   },
1353 #if ENABLE_RESERVED_CODE
1354   // (reserved for later use)
1355   {
1356     -1,                                 -1,
1357     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1358     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1359     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1360   },
1361 #endif
1362
1363   {
1364     -1,                                 -1,
1365     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1366     &xx_ei.use_gfx_element,             FALSE,
1367     &yy_ei.use_gfx_element
1368   },
1369   {
1370     -1,                                 -1,
1371     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1372     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1373     &yy_ei.gfx_element_initial
1374   },
1375
1376   {
1377     -1,                                 -1,
1378     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1379     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1380     &yy_ei.access_direction
1381   },
1382
1383   {
1384     -1,                                 -1,
1385     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1386     &xx_ei.collect_score_initial,       10,
1387     &yy_ei.collect_score_initial
1388   },
1389   {
1390     -1,                                 -1,
1391     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1392     &xx_ei.collect_count_initial,       1,
1393     &yy_ei.collect_count_initial
1394   },
1395
1396   {
1397     -1,                                 -1,
1398     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1399     &xx_ei.ce_value_fixed_initial,      0,
1400     &yy_ei.ce_value_fixed_initial
1401   },
1402   {
1403     -1,                                 -1,
1404     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1405     &xx_ei.ce_value_random_initial,     0,
1406     &yy_ei.ce_value_random_initial
1407   },
1408   {
1409     -1,                                 -1,
1410     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1411     &xx_ei.use_last_ce_value,           FALSE,
1412     &yy_ei.use_last_ce_value
1413   },
1414
1415   {
1416     -1,                                 -1,
1417     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1418     &xx_ei.push_delay_fixed,            8,
1419     &yy_ei.push_delay_fixed
1420   },
1421   {
1422     -1,                                 -1,
1423     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1424     &xx_ei.push_delay_random,           8,
1425     &yy_ei.push_delay_random
1426   },
1427   {
1428     -1,                                 -1,
1429     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1430     &xx_ei.drop_delay_fixed,            0,
1431     &yy_ei.drop_delay_fixed
1432   },
1433   {
1434     -1,                                 -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1436     &xx_ei.drop_delay_random,           0,
1437     &yy_ei.drop_delay_random
1438   },
1439   {
1440     -1,                                 -1,
1441     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1442     &xx_ei.move_delay_fixed,            0,
1443     &yy_ei.move_delay_fixed
1444   },
1445   {
1446     -1,                                 -1,
1447     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1448     &xx_ei.move_delay_random,           0,
1449     &yy_ei.move_delay_random
1450   },
1451   {
1452     -1,                                 -1,
1453     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1454     &xx_ei.step_delay_fixed,            0,
1455     &yy_ei.step_delay_fixed
1456   },
1457   {
1458     -1,                                 -1,
1459     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1460     &xx_ei.step_delay_random,           0,
1461     &yy_ei.step_delay_random
1462   },
1463
1464   {
1465     -1,                                 -1,
1466     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1467     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1468     &yy_ei.move_pattern
1469   },
1470   {
1471     -1,                                 -1,
1472     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1473     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1474     &yy_ei.move_direction_initial
1475   },
1476   {
1477     -1,                                 -1,
1478     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1479     &xx_ei.move_stepsize,               TILEX / 8,
1480     &yy_ei.move_stepsize
1481   },
1482
1483   {
1484     -1,                                 -1,
1485     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1486     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1487     &yy_ei.move_enter_element
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1492     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1493     &yy_ei.move_leave_element
1494   },
1495   {
1496     -1,                                 -1,
1497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1498     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1499     &yy_ei.move_leave_type
1500   },
1501
1502   {
1503     -1,                                 -1,
1504     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1505     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1506     &yy_ei.slippery_type
1507   },
1508
1509   {
1510     -1,                                 -1,
1511     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1512     &xx_ei.explosion_type,              EXPLODES_3X3,
1513     &yy_ei.explosion_type
1514   },
1515   {
1516     -1,                                 -1,
1517     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1518     &xx_ei.explosion_delay,             16,
1519     &yy_ei.explosion_delay
1520   },
1521   {
1522     -1,                                 -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1524     &xx_ei.ignition_delay,              8,
1525     &yy_ei.ignition_delay
1526   },
1527
1528   {
1529     -1,                                 -1,
1530     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1531     &xx_ei.content,                     EL_EMPTY_SPACE,
1532     &yy_ei.content,
1533     &xx_num_contents,                   1, 1
1534   },
1535
1536   // ---------- "num_change_pages" must be the last entry ---------------------
1537
1538   {
1539     -1,                                 SAVE_CONF_ALWAYS,
1540     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1541     &xx_ei.num_change_pages,            1,
1542     &yy_ei.num_change_pages
1543   },
1544
1545   {
1546     -1,                                 -1,
1547     -1,                                 -1,
1548     NULL,                               -1,
1549     NULL
1550   }
1551 };
1552
1553 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1554 {
1555   // ---------- "current_change_page" must be the first entry -----------------
1556
1557   {
1558     -1,                                 SAVE_CONF_ALWAYS,
1559     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1560     &xx_current_change_page,            -1
1561   },
1562
1563   // ---------- (the remaining entries can be in any order) -------------------
1564
1565   {
1566     -1,                                 -1,
1567     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1568     &xx_change.can_change,              FALSE
1569   },
1570
1571   {
1572     -1,                                 -1,
1573     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1574     &xx_event_bits[0],                  0
1575   },
1576   {
1577     -1,                                 -1,
1578     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1579     &xx_event_bits[1],                  0
1580   },
1581
1582   {
1583     -1,                                 -1,
1584     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1585     &xx_change.trigger_player,          CH_PLAYER_ANY
1586   },
1587   {
1588     -1,                                 -1,
1589     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1590     &xx_change.trigger_side,            CH_SIDE_ANY
1591   },
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1595     &xx_change.trigger_page,            CH_PAGE_ANY
1596   },
1597
1598   {
1599     -1,                                 -1,
1600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1601     &xx_change.target_element,          EL_EMPTY_SPACE
1602   },
1603
1604   {
1605     -1,                                 -1,
1606     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1607     &xx_change.delay_fixed,             0
1608   },
1609   {
1610     -1,                                 -1,
1611     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1612     &xx_change.delay_random,            0
1613   },
1614   {
1615     -1,                                 -1,
1616     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1617     &xx_change.delay_frames,            FRAMES_PER_SECOND
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1623     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1624   },
1625
1626   {
1627     -1,                                 -1,
1628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1629     &xx_change.explode,                 FALSE
1630   },
1631   {
1632     -1,                                 -1,
1633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1634     &xx_change.use_target_content,      FALSE
1635   },
1636   {
1637     -1,                                 -1,
1638     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1639     &xx_change.only_if_complete,        FALSE
1640   },
1641   {
1642     -1,                                 -1,
1643     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1644     &xx_change.use_random_replace,      FALSE
1645   },
1646   {
1647     -1,                                 -1,
1648     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1649     &xx_change.random_percentage,       100
1650   },
1651   {
1652     -1,                                 -1,
1653     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1654     &xx_change.replace_when,            CP_WHEN_EMPTY
1655   },
1656
1657   {
1658     -1,                                 -1,
1659     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1660     &xx_change.has_action,              FALSE
1661   },
1662   {
1663     -1,                                 -1,
1664     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1665     &xx_change.action_type,             CA_NO_ACTION
1666   },
1667   {
1668     -1,                                 -1,
1669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1670     &xx_change.action_mode,             CA_MODE_UNDEFINED
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1675     &xx_change.action_arg,              CA_ARG_UNDEFINED
1676   },
1677
1678   {
1679     -1,                                 -1,
1680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1681     &xx_change.action_element,          EL_EMPTY_SPACE
1682   },
1683
1684   {
1685     -1,                                 -1,
1686     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1687     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1688     &xx_num_contents,                   1, 1
1689   },
1690
1691   {
1692     -1,                                 -1,
1693     -1,                                 -1,
1694     NULL,                               -1
1695   }
1696 };
1697
1698 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1699 {
1700   {
1701     -1,                                 -1,
1702     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1703     &xx_ei.description[0],              -1, NULL,
1704     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1705     &xx_default_description[0]
1706   },
1707
1708   {
1709     -1,                                 -1,
1710     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1711     &xx_ei.use_gfx_element,             FALSE
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1716     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1717   },
1718
1719   {
1720     -1,                                 -1,
1721     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1722     &xx_group.choice_mode,              ANIM_RANDOM
1723   },
1724
1725   {
1726     -1,                                 -1,
1727     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1728     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1729     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1730   },
1731
1732   {
1733     -1,                                 -1,
1734     -1,                                 -1,
1735     NULL,                               -1
1736   }
1737 };
1738
1739 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1740 {
1741   {
1742     -1,                                 -1,
1743     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1744     &xx_ei.use_gfx_element,             FALSE
1745   },
1746   {
1747     -1,                                 -1,
1748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1749     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1750   },
1751
1752   {
1753     -1,                                 -1,
1754     -1,                                 -1,
1755     NULL,                               -1
1756   }
1757 };
1758
1759 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1760 {
1761   {
1762     EL_PLAYER_1,                        -1,
1763     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1764     &li.block_snap_field,               TRUE
1765   },
1766   {
1767     EL_PLAYER_1,                        -1,
1768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1769     &li.continuous_snapping,            TRUE
1770   },
1771   {
1772     EL_PLAYER_1,                        -1,
1773     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1774     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1775   },
1776   {
1777     EL_PLAYER_1,                        -1,
1778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1779     &li.use_start_element[0],           FALSE
1780   },
1781   {
1782     EL_PLAYER_1,                        -1,
1783     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1784     &li.start_element[0],               EL_PLAYER_1
1785   },
1786   {
1787     EL_PLAYER_1,                        -1,
1788     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1789     &li.use_artwork_element[0],         FALSE
1790   },
1791   {
1792     EL_PLAYER_1,                        -1,
1793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1794     &li.artwork_element[0],             EL_PLAYER_1
1795   },
1796   {
1797     EL_PLAYER_1,                        -1,
1798     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1799     &li.use_explosion_element[0],       FALSE
1800   },
1801   {
1802     EL_PLAYER_1,                        -1,
1803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1804     &li.explosion_element[0],           EL_PLAYER_1
1805   },
1806
1807   {
1808     -1,                                 -1,
1809     -1,                                 -1,
1810     NULL,                               -1
1811   }
1812 };
1813
1814 static struct
1815 {
1816   int filetype;
1817   char *id;
1818 }
1819 filetype_id_list[] =
1820 {
1821   { LEVEL_FILE_TYPE_RND,        "RND"   },
1822   { LEVEL_FILE_TYPE_BD,         "BD"    },
1823   { LEVEL_FILE_TYPE_EM,         "EM"    },
1824   { LEVEL_FILE_TYPE_SP,         "SP"    },
1825   { LEVEL_FILE_TYPE_DX,         "DX"    },
1826   { LEVEL_FILE_TYPE_SB,         "SB"    },
1827   { LEVEL_FILE_TYPE_DC,         "DC"    },
1828   { LEVEL_FILE_TYPE_MM,         "MM"    },
1829   { LEVEL_FILE_TYPE_MM,         "DF"    },
1830   { -1,                         NULL    },
1831 };
1832
1833
1834 // ============================================================================
1835 // level file functions
1836 // ============================================================================
1837
1838 static boolean check_special_flags(char *flag)
1839 {
1840   if (strEqual(options.special_flags, flag) ||
1841       strEqual(leveldir_current->special_flags, flag))
1842     return TRUE;
1843
1844   return FALSE;
1845 }
1846
1847 static struct DateInfo getCurrentDate(void)
1848 {
1849   time_t epoch_seconds = time(NULL);
1850   struct tm *now = localtime(&epoch_seconds);
1851   struct DateInfo date;
1852
1853   date.year  = now->tm_year + 1900;
1854   date.month = now->tm_mon  + 1;
1855   date.day   = now->tm_mday;
1856
1857   date.src   = DATE_SRC_CLOCK;
1858
1859   return date;
1860 }
1861
1862 static void resetEventFlags(struct ElementChangeInfo *change)
1863 {
1864   int i;
1865
1866   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1867     change->has_event[i] = FALSE;
1868 }
1869
1870 static void resetEventBits(void)
1871 {
1872   int i;
1873
1874   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1875     xx_event_bits[i] = 0;
1876 }
1877
1878 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1879 {
1880   int i;
1881
1882   /* important: only change event flag if corresponding event bit is set
1883      (this is because all xx_event_bits[] values are loaded separately,
1884      and all xx_event_bits[] values are set back to zero before loading
1885      another value xx_event_bits[x] (each value representing 32 flags)) */
1886
1887   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1888     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1889       change->has_event[i] = TRUE;
1890 }
1891
1892 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1893 {
1894   int i;
1895
1896   /* in contrast to the above function setEventFlagsFromEventBits(), it
1897      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1898      depending on the corresponding change->has_event[i] values here, as
1899      all xx_event_bits[] values are reset in resetEventBits() before */
1900
1901   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1902     if (change->has_event[i])
1903       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1904 }
1905
1906 static char *getDefaultElementDescription(struct ElementInfo *ei)
1907 {
1908   static char description[MAX_ELEMENT_NAME_LEN + 1];
1909   char *default_description = (ei->custom_description != NULL ?
1910                                ei->custom_description :
1911                                ei->editor_description);
1912   int i;
1913
1914   // always start with reliable default values
1915   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1916     description[i] = '\0';
1917
1918   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1919   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1920
1921   return &description[0];
1922 }
1923
1924 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1925 {
1926   char *default_description = getDefaultElementDescription(ei);
1927   int i;
1928
1929   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1930     ei->description[i] = default_description[i];
1931 }
1932
1933 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1934 {
1935   int i;
1936
1937   for (i = 0; conf[i].data_type != -1; i++)
1938   {
1939     int default_value = conf[i].default_value;
1940     int data_type = conf[i].data_type;
1941     int conf_type = conf[i].conf_type;
1942     int byte_mask = conf_type & CONF_MASK_BYTES;
1943
1944     if (byte_mask == CONF_MASK_MULTI_BYTES)
1945     {
1946       int default_num_entities = conf[i].default_num_entities;
1947       int max_num_entities = conf[i].max_num_entities;
1948
1949       *(int *)(conf[i].num_entities) = default_num_entities;
1950
1951       if (data_type == TYPE_STRING)
1952       {
1953         char *default_string = conf[i].default_string;
1954         char *string = (char *)(conf[i].value);
1955
1956         strncpy(string, default_string, max_num_entities);
1957       }
1958       else if (data_type == TYPE_ELEMENT_LIST)
1959       {
1960         int *element_array = (int *)(conf[i].value);
1961         int j;
1962
1963         for (j = 0; j < max_num_entities; j++)
1964           element_array[j] = default_value;
1965       }
1966       else if (data_type == TYPE_CONTENT_LIST)
1967       {
1968         struct Content *content = (struct Content *)(conf[i].value);
1969         int c, x, y;
1970
1971         for (c = 0; c < max_num_entities; c++)
1972           for (y = 0; y < 3; y++)
1973             for (x = 0; x < 3; x++)
1974               content[c].e[x][y] = default_value;
1975       }
1976     }
1977     else        // constant size configuration data (1, 2 or 4 bytes)
1978     {
1979       if (data_type == TYPE_BOOLEAN)
1980         *(boolean *)(conf[i].value) = default_value;
1981       else
1982         *(int *)    (conf[i].value) = default_value;
1983     }
1984   }
1985 }
1986
1987 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1988 {
1989   int i;
1990
1991   for (i = 0; conf[i].data_type != -1; i++)
1992   {
1993     int data_type = conf[i].data_type;
1994     int conf_type = conf[i].conf_type;
1995     int byte_mask = conf_type & CONF_MASK_BYTES;
1996
1997     if (byte_mask == CONF_MASK_MULTI_BYTES)
1998     {
1999       int max_num_entities = conf[i].max_num_entities;
2000
2001       if (data_type == TYPE_STRING)
2002       {
2003         char *string      = (char *)(conf[i].value);
2004         char *string_copy = (char *)(conf[i].value_copy);
2005
2006         strncpy(string_copy, string, max_num_entities);
2007       }
2008       else if (data_type == TYPE_ELEMENT_LIST)
2009       {
2010         int *element_array      = (int *)(conf[i].value);
2011         int *element_array_copy = (int *)(conf[i].value_copy);
2012         int j;
2013
2014         for (j = 0; j < max_num_entities; j++)
2015           element_array_copy[j] = element_array[j];
2016       }
2017       else if (data_type == TYPE_CONTENT_LIST)
2018       {
2019         struct Content *content      = (struct Content *)(conf[i].value);
2020         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2021         int c, x, y;
2022
2023         for (c = 0; c < max_num_entities; c++)
2024           for (y = 0; y < 3; y++)
2025             for (x = 0; x < 3; x++)
2026               content_copy[c].e[x][y] = content[c].e[x][y];
2027       }
2028     }
2029     else        // constant size configuration data (1, 2 or 4 bytes)
2030     {
2031       if (data_type == TYPE_BOOLEAN)
2032         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2033       else
2034         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2035     }
2036   }
2037 }
2038
2039 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2040 {
2041   int i;
2042
2043   xx_ei = *ei_from;     // copy element data into temporary buffer
2044   yy_ei = *ei_to;       // copy element data into temporary buffer
2045
2046   copyConfigFromConfigList(chunk_config_CUSX_base);
2047
2048   *ei_from = xx_ei;
2049   *ei_to   = yy_ei;
2050
2051   // ---------- reinitialize and copy change pages ----------
2052
2053   ei_to->num_change_pages = ei_from->num_change_pages;
2054   ei_to->current_change_page = ei_from->current_change_page;
2055
2056   setElementChangePages(ei_to, ei_to->num_change_pages);
2057
2058   for (i = 0; i < ei_to->num_change_pages; i++)
2059     ei_to->change_page[i] = ei_from->change_page[i];
2060
2061   // ---------- copy group element info ----------
2062   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2063     *ei_to->group = *ei_from->group;
2064
2065   // mark this custom element as modified
2066   ei_to->modified_settings = TRUE;
2067 }
2068
2069 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2070 {
2071   int change_page_size = sizeof(struct ElementChangeInfo);
2072
2073   ei->num_change_pages = MAX(1, change_pages);
2074
2075   ei->change_page =
2076     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2077
2078   if (ei->current_change_page >= ei->num_change_pages)
2079     ei->current_change_page = ei->num_change_pages - 1;
2080
2081   ei->change = &ei->change_page[ei->current_change_page];
2082 }
2083
2084 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2085 {
2086   xx_change = *change;          // copy change data into temporary buffer
2087
2088   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2089
2090   *change = xx_change;
2091
2092   resetEventFlags(change);
2093
2094   change->direct_action = 0;
2095   change->other_action = 0;
2096
2097   change->pre_change_function = NULL;
2098   change->change_function = NULL;
2099   change->post_change_function = NULL;
2100 }
2101
2102 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2103 {
2104   int i, x, y;
2105
2106   li = *level;          // copy level data into temporary buffer
2107   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2108   *level = li;          // copy temporary buffer back to level data
2109
2110   setLevelInfoToDefaults_BD();
2111   setLevelInfoToDefaults_EM();
2112   setLevelInfoToDefaults_SP();
2113   setLevelInfoToDefaults_MM();
2114
2115   level->native_bd_level = &native_bd_level;
2116   level->native_em_level = &native_em_level;
2117   level->native_sp_level = &native_sp_level;
2118   level->native_mm_level = &native_mm_level;
2119
2120   level->file_version = FILE_VERSION_ACTUAL;
2121   level->game_version = GAME_VERSION_ACTUAL;
2122
2123   level->creation_date = getCurrentDate();
2124
2125   level->encoding_16bit_field  = TRUE;
2126   level->encoding_16bit_yamyam = TRUE;
2127   level->encoding_16bit_amoeba = TRUE;
2128
2129   // clear level name and level author string buffers
2130   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2131     level->name[i] = '\0';
2132   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2133     level->author[i] = '\0';
2134
2135   // set level name and level author to default values
2136   strcpy(level->name, NAMELESS_LEVEL_NAME);
2137   strcpy(level->author, ANONYMOUS_NAME);
2138
2139   // set level playfield to playable default level with player and exit
2140   for (x = 0; x < MAX_LEV_FIELDX; x++)
2141     for (y = 0; y < MAX_LEV_FIELDY; y++)
2142       level->field[x][y] = EL_SAND;
2143
2144   level->field[0][0] = EL_PLAYER_1;
2145   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2146
2147   BorderElement = EL_STEELWALL;
2148
2149   // detect custom elements when loading them
2150   level->file_has_custom_elements = FALSE;
2151
2152   // set all bug compatibility flags to "false" => do not emulate this bug
2153   level->use_action_after_change_bug = FALSE;
2154
2155   if (leveldir_current)
2156   {
2157     // try to determine better author name than 'anonymous'
2158     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2159     {
2160       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2161       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2162     }
2163     else
2164     {
2165       switch (LEVELCLASS(leveldir_current))
2166       {
2167         case LEVELCLASS_TUTORIAL:
2168           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2169           break;
2170
2171         case LEVELCLASS_CONTRIB:
2172           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2173           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2174           break;
2175
2176         case LEVELCLASS_PRIVATE:
2177           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2178           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2179           break;
2180
2181         default:
2182           // keep default value
2183           break;
2184       }
2185     }
2186   }
2187 }
2188
2189 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2190 {
2191   static boolean clipboard_elements_initialized = FALSE;
2192   int i;
2193
2194   InitElementPropertiesStatic();
2195
2196   li = *level;          // copy level data into temporary buffer
2197   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2198   *level = li;          // copy temporary buffer back to level data
2199
2200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2201   {
2202     int element = i;
2203     struct ElementInfo *ei = &element_info[element];
2204
2205     if (element == EL_MM_GRAY_BALL)
2206     {
2207       struct LevelInfo_MM *level_mm = level->native_mm_level;
2208       int j;
2209
2210       for (j = 0; j < level->num_mm_ball_contents; j++)
2211         level->mm_ball_content[j] =
2212           map_element_MM_to_RND(level_mm->ball_content[j]);
2213     }
2214
2215     // never initialize clipboard elements after the very first time
2216     // (to be able to use clipboard elements between several levels)
2217     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2218       continue;
2219
2220     if (IS_ENVELOPE(element))
2221     {
2222       int envelope_nr = element - EL_ENVELOPE_1;
2223
2224       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2225
2226       level->envelope[envelope_nr] = xx_envelope;
2227     }
2228
2229     if (IS_CUSTOM_ELEMENT(element) ||
2230         IS_GROUP_ELEMENT(element) ||
2231         IS_INTERNAL_ELEMENT(element))
2232     {
2233       xx_ei = *ei;      // copy element data into temporary buffer
2234
2235       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2236
2237       *ei = xx_ei;
2238     }
2239
2240     setElementChangePages(ei, 1);
2241     setElementChangeInfoToDefaults(ei->change);
2242
2243     if (IS_CUSTOM_ELEMENT(element) ||
2244         IS_GROUP_ELEMENT(element))
2245     {
2246       setElementDescriptionToDefault(ei);
2247
2248       ei->modified_settings = FALSE;
2249     }
2250
2251     if (IS_CUSTOM_ELEMENT(element) ||
2252         IS_INTERNAL_ELEMENT(element))
2253     {
2254       // internal values used in level editor
2255
2256       ei->access_type = 0;
2257       ei->access_layer = 0;
2258       ei->access_protected = 0;
2259       ei->walk_to_action = 0;
2260       ei->smash_targets = 0;
2261       ei->deadliness = 0;
2262
2263       ei->can_explode_by_fire = FALSE;
2264       ei->can_explode_smashed = FALSE;
2265       ei->can_explode_impact = FALSE;
2266
2267       ei->current_change_page = 0;
2268     }
2269
2270     if (IS_GROUP_ELEMENT(element) ||
2271         IS_INTERNAL_ELEMENT(element))
2272     {
2273       struct ElementGroupInfo *group;
2274
2275       // initialize memory for list of elements in group
2276       if (ei->group == NULL)
2277         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2278
2279       group = ei->group;
2280
2281       xx_group = *group;        // copy group data into temporary buffer
2282
2283       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2284
2285       *group = xx_group;
2286     }
2287
2288     if (IS_EMPTY_ELEMENT(element) ||
2289         IS_INTERNAL_ELEMENT(element))
2290     {
2291       xx_ei = *ei;              // copy element data into temporary buffer
2292
2293       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2294
2295       *ei = xx_ei;
2296     }
2297   }
2298
2299   clipboard_elements_initialized = TRUE;
2300 }
2301
2302 static void setLevelInfoToDefaults(struct LevelInfo *level,
2303                                    boolean level_info_only,
2304                                    boolean reset_file_status)
2305 {
2306   setLevelInfoToDefaults_Level(level);
2307
2308   if (!level_info_only)
2309     setLevelInfoToDefaults_Elements(level);
2310
2311   if (reset_file_status)
2312   {
2313     level->no_valid_file = FALSE;
2314     level->no_level_file = FALSE;
2315   }
2316
2317   level->changed = FALSE;
2318 }
2319
2320 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2321 {
2322   level_file_info->nr = 0;
2323   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2324   level_file_info->packed = FALSE;
2325
2326   setString(&level_file_info->basename, NULL);
2327   setString(&level_file_info->filename, NULL);
2328 }
2329
2330 int getMappedElement_SB(int, boolean);
2331
2332 static void ActivateLevelTemplate(void)
2333 {
2334   int x, y;
2335
2336   if (check_special_flags("load_xsb_to_ces"))
2337   {
2338     // fill smaller playfields with padding "beyond border wall" elements
2339     if (level.fieldx < level_template.fieldx ||
2340         level.fieldy < level_template.fieldy)
2341     {
2342       short field[level.fieldx][level.fieldy];
2343       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2344       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2345       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2346       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2347
2348       // copy old playfield (which is smaller than the visible area)
2349       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2350         field[x][y] = level.field[x][y];
2351
2352       // fill new, larger playfield with "beyond border wall" elements
2353       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2354         level.field[x][y] = getMappedElement_SB('_', TRUE);
2355
2356       // copy the old playfield to the middle of the new playfield
2357       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2358         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2359
2360       level.fieldx = new_fieldx;
2361       level.fieldy = new_fieldy;
2362     }
2363   }
2364
2365   // Currently there is no special action needed to activate the template
2366   // data, because 'element_info' property settings overwrite the original
2367   // level data, while all other variables do not change.
2368
2369   // Exception: 'from_level_template' elements in the original level playfield
2370   // are overwritten with the corresponding elements at the same position in
2371   // playfield from the level template.
2372
2373   for (x = 0; x < level.fieldx; x++)
2374     for (y = 0; y < level.fieldy; y++)
2375       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2376         level.field[x][y] = level_template.field[x][y];
2377
2378   if (check_special_flags("load_xsb_to_ces"))
2379   {
2380     struct LevelInfo level_backup = level;
2381
2382     // overwrite all individual level settings from template level settings
2383     level = level_template;
2384
2385     // restore level file info
2386     level.file_info = level_backup.file_info;
2387
2388     // restore playfield size
2389     level.fieldx = level_backup.fieldx;
2390     level.fieldy = level_backup.fieldy;
2391
2392     // restore playfield content
2393     for (x = 0; x < level.fieldx; x++)
2394       for (y = 0; y < level.fieldy; y++)
2395         level.field[x][y] = level_backup.field[x][y];
2396
2397     // restore name and author from individual level
2398     strcpy(level.name,   level_backup.name);
2399     strcpy(level.author, level_backup.author);
2400
2401     // restore flag "use_custom_template"
2402     level.use_custom_template = level_backup.use_custom_template;
2403   }
2404 }
2405
2406 static boolean checkForPackageFromBasename_BD(char *basename)
2407 {
2408   // check for native BD level file extensions
2409   if (!strSuffixLower(basename, ".bd") &&
2410       !strSuffixLower(basename, ".bdr") &&
2411       !strSuffixLower(basename, ".brc") &&
2412       !strSuffixLower(basename, ".gds"))
2413     return FALSE;
2414
2415   // check for standard single-level BD files (like "001.bd")
2416   if (strSuffixLower(basename, ".bd") &&
2417       strlen(basename) == 6 &&
2418       basename[0] >= '0' && basename[0] <= '9' &&
2419       basename[1] >= '0' && basename[1] <= '9' &&
2420       basename[2] >= '0' && basename[2] <= '9')
2421     return FALSE;
2422
2423   // this is a level package in native BD file format
2424   return TRUE;
2425 }
2426
2427 static char *getLevelFilenameFromBasename(char *basename)
2428 {
2429   static char *filename = NULL;
2430
2431   checked_free(filename);
2432
2433   filename = getPath2(getCurrentLevelDir(), basename);
2434
2435   return filename;
2436 }
2437
2438 static int getFileTypeFromBasename(char *basename)
2439 {
2440   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2441
2442   static char *filename = NULL;
2443   struct stat file_status;
2444
2445   // ---------- try to determine file type from filename ----------
2446
2447   // check for typical filename of a Supaplex level package file
2448   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2449     return LEVEL_FILE_TYPE_SP;
2450
2451   // check for typical filename of a Diamond Caves II level package file
2452   if (strSuffixLower(basename, ".dc") ||
2453       strSuffixLower(basename, ".dc2"))
2454     return LEVEL_FILE_TYPE_DC;
2455
2456   // check for typical filename of a Sokoban level package file
2457   if (strSuffixLower(basename, ".xsb") &&
2458       strchr(basename, '%') == NULL)
2459     return LEVEL_FILE_TYPE_SB;
2460
2461   // check for typical filename of a Boulder Dash (GDash) level package file
2462   if (checkForPackageFromBasename_BD(basename))
2463     return LEVEL_FILE_TYPE_BD;
2464
2465   // ---------- try to determine file type from filesize ----------
2466
2467   checked_free(filename);
2468   filename = getPath2(getCurrentLevelDir(), basename);
2469
2470   if (stat(filename, &file_status) == 0)
2471   {
2472     // check for typical filesize of a Supaplex level package file
2473     if (file_status.st_size == 170496)
2474       return LEVEL_FILE_TYPE_SP;
2475   }
2476
2477   return LEVEL_FILE_TYPE_UNKNOWN;
2478 }
2479
2480 static int getFileTypeFromMagicBytes(char *filename, int type)
2481 {
2482   File *file;
2483
2484   if ((file = openFile(filename, MODE_READ)))
2485   {
2486     char chunk_name[CHUNK_ID_LEN + 1];
2487
2488     getFileChunkBE(file, chunk_name, NULL);
2489
2490     if (strEqual(chunk_name, "MMII") ||
2491         strEqual(chunk_name, "MIRR"))
2492       type = LEVEL_FILE_TYPE_MM;
2493
2494     closeFile(file);
2495   }
2496
2497   return type;
2498 }
2499
2500 static boolean checkForPackageFromBasename(char *basename)
2501 {
2502   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2503   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2504
2505   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2506 }
2507
2508 static char *getSingleLevelBasenameExt(int nr, char *extension)
2509 {
2510   static char basename[MAX_FILENAME_LEN];
2511
2512   if (nr < 0)
2513     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2514   else
2515     sprintf(basename, "%03d.%s", nr, extension);
2516
2517   return basename;
2518 }
2519
2520 static char *getSingleLevelBasename(int nr)
2521 {
2522   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2523 }
2524
2525 static char *getPackedLevelBasename(int type)
2526 {
2527   static char basename[MAX_FILENAME_LEN];
2528   char *directory = getCurrentLevelDir();
2529   Directory *dir;
2530   DirectoryEntry *dir_entry;
2531
2532   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2533
2534   if ((dir = openDirectory(directory)) == NULL)
2535   {
2536     Warn("cannot read current level directory '%s'", directory);
2537
2538     return basename;
2539   }
2540
2541   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2542   {
2543     char *entry_basename = dir_entry->basename;
2544     int entry_type = getFileTypeFromBasename(entry_basename);
2545
2546     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2547     {
2548       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2549           type == entry_type)
2550       {
2551         strcpy(basename, entry_basename);
2552
2553         break;
2554       }
2555     }
2556   }
2557
2558   closeDirectory(dir);
2559
2560   return basename;
2561 }
2562
2563 static char *getSingleLevelFilename(int nr)
2564 {
2565   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2566 }
2567
2568 #if ENABLE_UNUSED_CODE
2569 static char *getPackedLevelFilename(int type)
2570 {
2571   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2572 }
2573 #endif
2574
2575 char *getDefaultLevelFilename(int nr)
2576 {
2577   return getSingleLevelFilename(nr);
2578 }
2579
2580 #if ENABLE_UNUSED_CODE
2581 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2582                                                  int type)
2583 {
2584   lfi->type = type;
2585   lfi->packed = FALSE;
2586
2587   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2588   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2589 }
2590 #endif
2591
2592 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2593                                                  int type, char *format, ...)
2594 {
2595   static char basename[MAX_FILENAME_LEN];
2596   va_list ap;
2597
2598   va_start(ap, format);
2599   vsprintf(basename, format, ap);
2600   va_end(ap);
2601
2602   lfi->type = type;
2603   lfi->packed = FALSE;
2604
2605   setString(&lfi->basename, basename);
2606   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2607 }
2608
2609 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2610                                                  int type)
2611 {
2612   lfi->type = type;
2613   lfi->packed = TRUE;
2614
2615   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2616   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2617 }
2618
2619 static int getFiletypeFromID(char *filetype_id)
2620 {
2621   char *filetype_id_lower;
2622   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2623   int i;
2624
2625   if (filetype_id == NULL)
2626     return LEVEL_FILE_TYPE_UNKNOWN;
2627
2628   filetype_id_lower = getStringToLower(filetype_id);
2629
2630   for (i = 0; filetype_id_list[i].id != NULL; i++)
2631   {
2632     char *id_lower = getStringToLower(filetype_id_list[i].id);
2633     
2634     if (strEqual(filetype_id_lower, id_lower))
2635       filetype = filetype_id_list[i].filetype;
2636
2637     free(id_lower);
2638
2639     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2640       break;
2641   }
2642
2643   free(filetype_id_lower);
2644
2645   return filetype;
2646 }
2647
2648 char *getLocalLevelTemplateFilename(void)
2649 {
2650   return getDefaultLevelFilename(-1);
2651 }
2652
2653 char *getGlobalLevelTemplateFilename(void)
2654 {
2655   // global variable "leveldir_current" must be modified in the loop below
2656   LevelDirTree *leveldir_current_last = leveldir_current;
2657   char *filename = NULL;
2658
2659   // check for template level in path from current to topmost tree node
2660
2661   while (leveldir_current != NULL)
2662   {
2663     filename = getDefaultLevelFilename(-1);
2664
2665     if (fileExists(filename))
2666       break;
2667
2668     leveldir_current = leveldir_current->node_parent;
2669   }
2670
2671   // restore global variable "leveldir_current" modified in above loop
2672   leveldir_current = leveldir_current_last;
2673
2674   return filename;
2675 }
2676
2677 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2678 {
2679   int nr = lfi->nr;
2680
2681   // special case: level number is negative => check for level template file
2682   if (nr < 0)
2683   {
2684     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2685                                          getSingleLevelBasename(-1));
2686
2687     // replace local level template filename with global template filename
2688     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2689
2690     // no fallback if template file not existing
2691     return;
2692   }
2693
2694   // special case: check for file name/pattern specified in "levelinfo.conf"
2695   if (leveldir_current->level_filename != NULL)
2696   {
2697     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2698
2699     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2700                                          leveldir_current->level_filename, nr);
2701
2702     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2703
2704     if (fileExists(lfi->filename))
2705       return;
2706   }
2707   else if (leveldir_current->level_filetype != NULL)
2708   {
2709     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2710
2711     // check for specified native level file with standard file name
2712     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2713                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2714     if (fileExists(lfi->filename))
2715       return;
2716   }
2717
2718   // check for native Rocks'n'Diamonds level file
2719   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2720                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2721   if (fileExists(lfi->filename))
2722     return;
2723
2724   // check for native Boulder Dash level file
2725   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2726   if (fileExists(lfi->filename))
2727     return;
2728
2729   // check for Emerald Mine level file (V1)
2730   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2731                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2732   if (fileExists(lfi->filename))
2733     return;
2734   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2735                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2736   if (fileExists(lfi->filename))
2737     return;
2738
2739   // check for Emerald Mine level file (V2 to V5)
2740   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2741   if (fileExists(lfi->filename))
2742     return;
2743
2744   // check for Emerald Mine level file (V6 / single mode)
2745   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2746   if (fileExists(lfi->filename))
2747     return;
2748   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2749   if (fileExists(lfi->filename))
2750     return;
2751
2752   // check for Emerald Mine level file (V6 / teamwork mode)
2753   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2754   if (fileExists(lfi->filename))
2755     return;
2756   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2757   if (fileExists(lfi->filename))
2758     return;
2759
2760   // check for various packed level file formats
2761   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2762   if (fileExists(lfi->filename))
2763     return;
2764
2765   // no known level file found -- use default values (and fail later)
2766   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2767                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2768 }
2769
2770 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2771 {
2772   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2773     lfi->type = getFileTypeFromBasename(lfi->basename);
2774
2775   if (lfi->type == LEVEL_FILE_TYPE_RND)
2776     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2777 }
2778
2779 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2780 {
2781   // always start with reliable default values
2782   setFileInfoToDefaults(level_file_info);
2783
2784   level_file_info->nr = nr;     // set requested level number
2785
2786   determineLevelFileInfo_Filename(level_file_info);
2787   determineLevelFileInfo_Filetype(level_file_info);
2788 }
2789
2790 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2791                               struct LevelFileInfo *lfi_to)
2792 {
2793   lfi_to->nr     = lfi_from->nr;
2794   lfi_to->type   = lfi_from->type;
2795   lfi_to->packed = lfi_from->packed;
2796
2797   setString(&lfi_to->basename, lfi_from->basename);
2798   setString(&lfi_to->filename, lfi_from->filename);
2799 }
2800
2801 // ----------------------------------------------------------------------------
2802 // functions for loading R'n'D level
2803 // ----------------------------------------------------------------------------
2804
2805 int getMappedElement(int element)
2806 {
2807   // remap some (historic, now obsolete) elements
2808
2809   switch (element)
2810   {
2811     case EL_PLAYER_OBSOLETE:
2812       element = EL_PLAYER_1;
2813       break;
2814
2815     case EL_KEY_OBSOLETE:
2816       element = EL_KEY_1;
2817       break;
2818
2819     case EL_EM_KEY_1_FILE_OBSOLETE:
2820       element = EL_EM_KEY_1;
2821       break;
2822
2823     case EL_EM_KEY_2_FILE_OBSOLETE:
2824       element = EL_EM_KEY_2;
2825       break;
2826
2827     case EL_EM_KEY_3_FILE_OBSOLETE:
2828       element = EL_EM_KEY_3;
2829       break;
2830
2831     case EL_EM_KEY_4_FILE_OBSOLETE:
2832       element = EL_EM_KEY_4;
2833       break;
2834
2835     case EL_ENVELOPE_OBSOLETE:
2836       element = EL_ENVELOPE_1;
2837       break;
2838
2839     case EL_SP_EMPTY:
2840       element = EL_EMPTY;
2841       break;
2842
2843     default:
2844       if (element >= NUM_FILE_ELEMENTS)
2845       {
2846         Warn("invalid level element %d", element);
2847
2848         element = EL_UNKNOWN;
2849       }
2850       break;
2851   }
2852
2853   return element;
2854 }
2855
2856 static int getMappedElementByVersion(int element, int game_version)
2857 {
2858   // remap some elements due to certain game version
2859
2860   if (game_version <= VERSION_IDENT(2,2,0,0))
2861   {
2862     // map game font elements
2863     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2864                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2865                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2866                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2867   }
2868
2869   if (game_version < VERSION_IDENT(3,0,0,0))
2870   {
2871     // map Supaplex gravity tube elements
2872     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2873                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2874                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2875                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2876                element);
2877   }
2878
2879   return element;
2880 }
2881
2882 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2883 {
2884   level->file_version = getFileVersion(file);
2885   level->game_version = getFileVersion(file);
2886
2887   return chunk_size;
2888 }
2889
2890 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2891 {
2892   level->creation_date.year  = getFile16BitBE(file);
2893   level->creation_date.month = getFile8Bit(file);
2894   level->creation_date.day   = getFile8Bit(file);
2895
2896   level->creation_date.src   = DATE_SRC_LEVELFILE;
2897
2898   return chunk_size;
2899 }
2900
2901 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2902 {
2903   int initial_player_stepsize;
2904   int initial_player_gravity;
2905   int i, x, y;
2906
2907   level->fieldx = getFile8Bit(file);
2908   level->fieldy = getFile8Bit(file);
2909
2910   level->time           = getFile16BitBE(file);
2911   level->gems_needed    = getFile16BitBE(file);
2912
2913   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2914     level->name[i] = getFile8Bit(file);
2915   level->name[MAX_LEVEL_NAME_LEN] = 0;
2916
2917   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2918     level->score[i] = getFile8Bit(file);
2919
2920   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2921   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2922     for (y = 0; y < 3; y++)
2923       for (x = 0; x < 3; x++)
2924         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2925
2926   level->amoeba_speed           = getFile8Bit(file);
2927   level->time_magic_wall        = getFile8Bit(file);
2928   level->time_wheel             = getFile8Bit(file);
2929   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2930
2931   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2932                                    STEPSIZE_NORMAL);
2933
2934   for (i = 0; i < MAX_PLAYERS; i++)
2935     level->initial_player_stepsize[i] = initial_player_stepsize;
2936
2937   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2938
2939   for (i = 0; i < MAX_PLAYERS; i++)
2940     level->initial_player_gravity[i] = initial_player_gravity;
2941
2942   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2943   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2944
2945   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2946
2947   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2948   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2949   level->can_move_into_acid_bits = getFile32BitBE(file);
2950   level->dont_collide_with_bits = getFile8Bit(file);
2951
2952   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2953   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2954
2955   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2956   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2957   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2958
2959   level->game_engine_type       = getFile8Bit(file);
2960
2961   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2962
2963   return chunk_size;
2964 }
2965
2966 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2967 {
2968   int i;
2969
2970   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2971     level->name[i] = getFile8Bit(file);
2972   level->name[MAX_LEVEL_NAME_LEN] = 0;
2973
2974   return chunk_size;
2975 }
2976
2977 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2978 {
2979   int i;
2980
2981   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2982     level->author[i] = getFile8Bit(file);
2983   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2984
2985   return chunk_size;
2986 }
2987
2988 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2989 {
2990   int x, y;
2991   int chunk_size_expected = level->fieldx * level->fieldy;
2992
2993   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2994      stored with 16-bit encoding (and should be twice as big then).
2995      Even worse, playfield data was stored 16-bit when only yamyam content
2996      contained 16-bit elements and vice versa. */
2997
2998   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2999     chunk_size_expected *= 2;
3000
3001   if (chunk_size_expected != chunk_size)
3002   {
3003     ReadUnusedBytesFromFile(file, chunk_size);
3004     return chunk_size_expected;
3005   }
3006
3007   for (y = 0; y < level->fieldy; y++)
3008     for (x = 0; x < level->fieldx; x++)
3009       level->field[x][y] =
3010         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3011                          getFile8Bit(file));
3012   return chunk_size;
3013 }
3014
3015 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3016 {
3017   int i, x, y;
3018   int header_size = 4;
3019   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3020   int chunk_size_expected = header_size + content_size;
3021
3022   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3023      stored with 16-bit encoding (and should be twice as big then).
3024      Even worse, playfield data was stored 16-bit when only yamyam content
3025      contained 16-bit elements and vice versa. */
3026
3027   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3028     chunk_size_expected += content_size;
3029
3030   if (chunk_size_expected != chunk_size)
3031   {
3032     ReadUnusedBytesFromFile(file, chunk_size);
3033     return chunk_size_expected;
3034   }
3035
3036   getFile8Bit(file);
3037   level->num_yamyam_contents = getFile8Bit(file);
3038   getFile8Bit(file);
3039   getFile8Bit(file);
3040
3041   // correct invalid number of content fields -- should never happen
3042   if (level->num_yamyam_contents < 1 ||
3043       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3044     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3045
3046   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3047     for (y = 0; y < 3; y++)
3048       for (x = 0; x < 3; x++)
3049         level->yamyam_content[i].e[x][y] =
3050           getMappedElement(level->encoding_16bit_field ?
3051                            getFile16BitBE(file) : getFile8Bit(file));
3052   return chunk_size;
3053 }
3054
3055 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3056 {
3057   int i, x, y;
3058   int element;
3059   int num_contents;
3060   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3061
3062   element = getMappedElement(getFile16BitBE(file));
3063   num_contents = getFile8Bit(file);
3064
3065   getFile8Bit(file);    // content x size (unused)
3066   getFile8Bit(file);    // content y size (unused)
3067
3068   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3069
3070   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3071     for (y = 0; y < 3; y++)
3072       for (x = 0; x < 3; x++)
3073         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3074
3075   // correct invalid number of content fields -- should never happen
3076   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3077     num_contents = STD_ELEMENT_CONTENTS;
3078
3079   if (element == EL_YAMYAM)
3080   {
3081     level->num_yamyam_contents = num_contents;
3082
3083     for (i = 0; i < num_contents; i++)
3084       for (y = 0; y < 3; y++)
3085         for (x = 0; x < 3; x++)
3086           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3087   }
3088   else if (element == EL_BD_AMOEBA)
3089   {
3090     level->amoeba_content = content_array[0][0][0];
3091   }
3092   else
3093   {
3094     Warn("cannot load content for element '%d'", element);
3095   }
3096
3097   return chunk_size;
3098 }
3099
3100 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3101 {
3102   int i;
3103   int element;
3104   int envelope_nr;
3105   int envelope_len;
3106   int chunk_size_expected;
3107
3108   element = getMappedElement(getFile16BitBE(file));
3109   if (!IS_ENVELOPE(element))
3110     element = EL_ENVELOPE_1;
3111
3112   envelope_nr = element - EL_ENVELOPE_1;
3113
3114   envelope_len = getFile16BitBE(file);
3115
3116   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3117   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3118
3119   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3120
3121   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3122   if (chunk_size_expected != chunk_size)
3123   {
3124     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3125     return chunk_size_expected;
3126   }
3127
3128   for (i = 0; i < envelope_len; i++)
3129     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3130
3131   return chunk_size;
3132 }
3133
3134 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3135 {
3136   int num_changed_custom_elements = getFile16BitBE(file);
3137   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3138   int i;
3139
3140   if (chunk_size_expected != chunk_size)
3141   {
3142     ReadUnusedBytesFromFile(file, chunk_size - 2);
3143     return chunk_size_expected;
3144   }
3145
3146   for (i = 0; i < num_changed_custom_elements; i++)
3147   {
3148     int element = getMappedElement(getFile16BitBE(file));
3149     int properties = getFile32BitBE(file);
3150
3151     if (IS_CUSTOM_ELEMENT(element))
3152       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3153     else
3154       Warn("invalid custom element number %d", element);
3155
3156     // older game versions that wrote level files with CUS1 chunks used
3157     // different default push delay values (not yet stored in level file)
3158     element_info[element].push_delay_fixed = 2;
3159     element_info[element].push_delay_random = 8;
3160   }
3161
3162   level->file_has_custom_elements = TRUE;
3163
3164   return chunk_size;
3165 }
3166
3167 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3168 {
3169   int num_changed_custom_elements = getFile16BitBE(file);
3170   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3171   int i;
3172
3173   if (chunk_size_expected != chunk_size)
3174   {
3175     ReadUnusedBytesFromFile(file, chunk_size - 2);
3176     return chunk_size_expected;
3177   }
3178
3179   for (i = 0; i < num_changed_custom_elements; i++)
3180   {
3181     int element = getMappedElement(getFile16BitBE(file));
3182     int custom_target_element = getMappedElement(getFile16BitBE(file));
3183
3184     if (IS_CUSTOM_ELEMENT(element))
3185       element_info[element].change->target_element = custom_target_element;
3186     else
3187       Warn("invalid custom element number %d", element);
3188   }
3189
3190   level->file_has_custom_elements = TRUE;
3191
3192   return chunk_size;
3193 }
3194
3195 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3196 {
3197   int num_changed_custom_elements = getFile16BitBE(file);
3198   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3199   int i, j, x, y;
3200
3201   if (chunk_size_expected != chunk_size)
3202   {
3203     ReadUnusedBytesFromFile(file, chunk_size - 2);
3204     return chunk_size_expected;
3205   }
3206
3207   for (i = 0; i < num_changed_custom_elements; i++)
3208   {
3209     int element = getMappedElement(getFile16BitBE(file));
3210     struct ElementInfo *ei = &element_info[element];
3211     unsigned int event_bits;
3212
3213     if (!IS_CUSTOM_ELEMENT(element))
3214     {
3215       Warn("invalid custom element number %d", element);
3216
3217       element = EL_INTERNAL_DUMMY;
3218     }
3219
3220     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3221       ei->description[j] = getFile8Bit(file);
3222     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3223
3224     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3225
3226     // some free bytes for future properties and padding
3227     ReadUnusedBytesFromFile(file, 7);
3228
3229     ei->use_gfx_element = getFile8Bit(file);
3230     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3231
3232     ei->collect_score_initial = getFile8Bit(file);
3233     ei->collect_count_initial = getFile8Bit(file);
3234
3235     ei->push_delay_fixed = getFile16BitBE(file);
3236     ei->push_delay_random = getFile16BitBE(file);
3237     ei->move_delay_fixed = getFile16BitBE(file);
3238     ei->move_delay_random = getFile16BitBE(file);
3239
3240     ei->move_pattern = getFile16BitBE(file);
3241     ei->move_direction_initial = getFile8Bit(file);
3242     ei->move_stepsize = getFile8Bit(file);
3243
3244     for (y = 0; y < 3; y++)
3245       for (x = 0; x < 3; x++)
3246         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3247
3248     // bits 0 - 31 of "has_event[]"
3249     event_bits = getFile32BitBE(file);
3250     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3251       if (event_bits & (1u << j))
3252         ei->change->has_event[j] = TRUE;
3253
3254     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3255
3256     ei->change->delay_fixed = getFile16BitBE(file);
3257     ei->change->delay_random = getFile16BitBE(file);
3258     ei->change->delay_frames = getFile16BitBE(file);
3259
3260     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3261
3262     ei->change->explode = getFile8Bit(file);
3263     ei->change->use_target_content = getFile8Bit(file);
3264     ei->change->only_if_complete = getFile8Bit(file);
3265     ei->change->use_random_replace = getFile8Bit(file);
3266
3267     ei->change->random_percentage = getFile8Bit(file);
3268     ei->change->replace_when = getFile8Bit(file);
3269
3270     for (y = 0; y < 3; y++)
3271       for (x = 0; x < 3; x++)
3272         ei->change->target_content.e[x][y] =
3273           getMappedElement(getFile16BitBE(file));
3274
3275     ei->slippery_type = getFile8Bit(file);
3276
3277     // some free bytes for future properties and padding
3278     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3279
3280     // mark that this custom element has been modified
3281     ei->modified_settings = TRUE;
3282   }
3283
3284   level->file_has_custom_elements = TRUE;
3285
3286   return chunk_size;
3287 }
3288
3289 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3290 {
3291   struct ElementInfo *ei;
3292   int chunk_size_expected;
3293   int element;
3294   int i, j, x, y;
3295
3296   // ---------- custom element base property values (96 bytes) ----------------
3297
3298   element = getMappedElement(getFile16BitBE(file));
3299
3300   if (!IS_CUSTOM_ELEMENT(element))
3301   {
3302     Warn("invalid custom element number %d", element);
3303
3304     ReadUnusedBytesFromFile(file, chunk_size - 2);
3305
3306     return chunk_size;
3307   }
3308
3309   ei = &element_info[element];
3310
3311   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3312     ei->description[i] = getFile8Bit(file);
3313   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3314
3315   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3316
3317   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3318
3319   ei->num_change_pages = getFile8Bit(file);
3320
3321   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3322   if (chunk_size_expected != chunk_size)
3323   {
3324     ReadUnusedBytesFromFile(file, chunk_size - 43);
3325     return chunk_size_expected;
3326   }
3327
3328   ei->ce_value_fixed_initial = getFile16BitBE(file);
3329   ei->ce_value_random_initial = getFile16BitBE(file);
3330   ei->use_last_ce_value = getFile8Bit(file);
3331
3332   ei->use_gfx_element = getFile8Bit(file);
3333   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3334
3335   ei->collect_score_initial = getFile8Bit(file);
3336   ei->collect_count_initial = getFile8Bit(file);
3337
3338   ei->drop_delay_fixed = getFile8Bit(file);
3339   ei->push_delay_fixed = getFile8Bit(file);
3340   ei->drop_delay_random = getFile8Bit(file);
3341   ei->push_delay_random = getFile8Bit(file);
3342   ei->move_delay_fixed = getFile16BitBE(file);
3343   ei->move_delay_random = getFile16BitBE(file);
3344
3345   // bits 0 - 15 of "move_pattern" ...
3346   ei->move_pattern = getFile16BitBE(file);
3347   ei->move_direction_initial = getFile8Bit(file);
3348   ei->move_stepsize = getFile8Bit(file);
3349
3350   ei->slippery_type = getFile8Bit(file);
3351
3352   for (y = 0; y < 3; y++)
3353     for (x = 0; x < 3; x++)
3354       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3355
3356   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3357   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3358   ei->move_leave_type = getFile8Bit(file);
3359
3360   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3361   ei->move_pattern |= (getFile16BitBE(file) << 16);
3362
3363   ei->access_direction = getFile8Bit(file);
3364
3365   ei->explosion_delay = getFile8Bit(file);
3366   ei->ignition_delay = getFile8Bit(file);
3367   ei->explosion_type = getFile8Bit(file);
3368
3369   // some free bytes for future custom property values and padding
3370   ReadUnusedBytesFromFile(file, 1);
3371
3372   // ---------- change page property values (48 bytes) ------------------------
3373
3374   setElementChangePages(ei, ei->num_change_pages);
3375
3376   for (i = 0; i < ei->num_change_pages; i++)
3377   {
3378     struct ElementChangeInfo *change = &ei->change_page[i];
3379     unsigned int event_bits;
3380
3381     // always start with reliable default values
3382     setElementChangeInfoToDefaults(change);
3383
3384     // bits 0 - 31 of "has_event[]" ...
3385     event_bits = getFile32BitBE(file);
3386     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3387       if (event_bits & (1u << j))
3388         change->has_event[j] = TRUE;
3389
3390     change->target_element = getMappedElement(getFile16BitBE(file));
3391
3392     change->delay_fixed = getFile16BitBE(file);
3393     change->delay_random = getFile16BitBE(file);
3394     change->delay_frames = getFile16BitBE(file);
3395
3396     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3397
3398     change->explode = getFile8Bit(file);
3399     change->use_target_content = getFile8Bit(file);
3400     change->only_if_complete = getFile8Bit(file);
3401     change->use_random_replace = getFile8Bit(file);
3402
3403     change->random_percentage = getFile8Bit(file);
3404     change->replace_when = getFile8Bit(file);
3405
3406     for (y = 0; y < 3; y++)
3407       for (x = 0; x < 3; x++)
3408         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3409
3410     change->can_change = getFile8Bit(file);
3411
3412     change->trigger_side = getFile8Bit(file);
3413
3414     change->trigger_player = getFile8Bit(file);
3415     change->trigger_page = getFile8Bit(file);
3416
3417     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3418                             CH_PAGE_ANY : (1 << change->trigger_page));
3419
3420     change->has_action = getFile8Bit(file);
3421     change->action_type = getFile8Bit(file);
3422     change->action_mode = getFile8Bit(file);
3423     change->action_arg = getFile16BitBE(file);
3424
3425     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3426     event_bits = getFile8Bit(file);
3427     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3428       if (event_bits & (1u << (j - 32)))
3429         change->has_event[j] = TRUE;
3430   }
3431
3432   // mark this custom element as modified
3433   ei->modified_settings = TRUE;
3434
3435   level->file_has_custom_elements = TRUE;
3436
3437   return chunk_size;
3438 }
3439
3440 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3441 {
3442   struct ElementInfo *ei;
3443   struct ElementGroupInfo *group;
3444   int element;
3445   int i;
3446
3447   element = getMappedElement(getFile16BitBE(file));
3448
3449   if (!IS_GROUP_ELEMENT(element))
3450   {
3451     Warn("invalid group element number %d", element);
3452
3453     ReadUnusedBytesFromFile(file, chunk_size - 2);
3454
3455     return chunk_size;
3456   }
3457
3458   ei = &element_info[element];
3459
3460   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3461     ei->description[i] = getFile8Bit(file);
3462   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3463
3464   group = element_info[element].group;
3465
3466   group->num_elements = getFile8Bit(file);
3467
3468   ei->use_gfx_element = getFile8Bit(file);
3469   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3470
3471   group->choice_mode = getFile8Bit(file);
3472
3473   // some free bytes for future values and padding
3474   ReadUnusedBytesFromFile(file, 3);
3475
3476   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3477     group->element[i] = getMappedElement(getFile16BitBE(file));
3478
3479   // mark this group element as modified
3480   element_info[element].modified_settings = TRUE;
3481
3482   level->file_has_custom_elements = TRUE;
3483
3484   return chunk_size;
3485 }
3486
3487 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3488                                 int element, int real_element)
3489 {
3490   int micro_chunk_size = 0;
3491   int conf_type = getFile8Bit(file);
3492   int byte_mask = conf_type & CONF_MASK_BYTES;
3493   boolean element_found = FALSE;
3494   int i;
3495
3496   micro_chunk_size += 1;
3497
3498   if (byte_mask == CONF_MASK_MULTI_BYTES)
3499   {
3500     int num_bytes = getFile16BitBE(file);
3501     byte *buffer = checked_malloc(num_bytes);
3502
3503     ReadBytesFromFile(file, buffer, num_bytes);
3504
3505     for (i = 0; conf[i].data_type != -1; i++)
3506     {
3507       if (conf[i].element == element &&
3508           conf[i].conf_type == conf_type)
3509       {
3510         int data_type = conf[i].data_type;
3511         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3512         int max_num_entities = conf[i].max_num_entities;
3513
3514         if (num_entities > max_num_entities)
3515         {
3516           Warn("truncating number of entities for element %d from %d to %d",
3517                element, num_entities, max_num_entities);
3518
3519           num_entities = max_num_entities;
3520         }
3521
3522         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3523                                   data_type == TYPE_CONTENT_LIST))
3524         {
3525           // for element and content lists, zero entities are not allowed
3526           Warn("found empty list of entities for element %d", element);
3527
3528           // do not set "num_entities" here to prevent reading behind buffer
3529
3530           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3531         }
3532         else
3533         {
3534           *(int *)(conf[i].num_entities) = num_entities;
3535         }
3536
3537         element_found = TRUE;
3538
3539         if (data_type == TYPE_STRING)
3540         {
3541           char *string = (char *)(conf[i].value);
3542           int j;
3543
3544           for (j = 0; j < max_num_entities; j++)
3545             string[j] = (j < num_entities ? buffer[j] : '\0');
3546         }
3547         else if (data_type == TYPE_ELEMENT_LIST)
3548         {
3549           int *element_array = (int *)(conf[i].value);
3550           int j;
3551
3552           for (j = 0; j < num_entities; j++)
3553             element_array[j] =
3554               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3555         }
3556         else if (data_type == TYPE_CONTENT_LIST)
3557         {
3558           struct Content *content= (struct Content *)(conf[i].value);
3559           int c, x, y;
3560
3561           for (c = 0; c < num_entities; c++)
3562             for (y = 0; y < 3; y++)
3563               for (x = 0; x < 3; x++)
3564                 content[c].e[x][y] =
3565                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3566         }
3567         else
3568           element_found = FALSE;
3569
3570         break;
3571       }
3572     }
3573
3574     checked_free(buffer);
3575
3576     micro_chunk_size += 2 + num_bytes;
3577   }
3578   else          // constant size configuration data (1, 2 or 4 bytes)
3579   {
3580     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3581                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3582                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3583
3584     for (i = 0; conf[i].data_type != -1; i++)
3585     {
3586       if (conf[i].element == element &&
3587           conf[i].conf_type == conf_type)
3588       {
3589         int data_type = conf[i].data_type;
3590
3591         if (data_type == TYPE_ELEMENT)
3592           value = getMappedElement(value);
3593
3594         if (data_type == TYPE_BOOLEAN)
3595           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3596         else
3597           *(int *)    (conf[i].value) = value;
3598
3599         element_found = TRUE;
3600
3601         break;
3602       }
3603     }
3604
3605     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3606   }
3607
3608   if (!element_found)
3609   {
3610     char *error_conf_chunk_bytes =
3611       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3612        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3613        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3614     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3615     int error_element = real_element;
3616
3617     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3618          error_conf_chunk_bytes, error_conf_chunk_token,
3619          error_element, EL_NAME(error_element));
3620   }
3621
3622   return micro_chunk_size;
3623 }
3624
3625 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3626 {
3627   int real_chunk_size = 0;
3628
3629   li = *level;          // copy level data into temporary buffer
3630
3631   while (!checkEndOfFile(file))
3632   {
3633     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3634
3635     if (real_chunk_size >= chunk_size)
3636       break;
3637   }
3638
3639   *level = li;          // copy temporary buffer back to level data
3640
3641   return real_chunk_size;
3642 }
3643
3644 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3645 {
3646   int real_chunk_size = 0;
3647
3648   li = *level;          // copy level data into temporary buffer
3649
3650   while (!checkEndOfFile(file))
3651   {
3652     int element = getMappedElement(getFile16BitBE(file));
3653
3654     real_chunk_size += 2;
3655     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3656                                             element, element);
3657     if (real_chunk_size >= chunk_size)
3658       break;
3659   }
3660
3661   *level = li;          // copy temporary buffer back to level data
3662
3663   return real_chunk_size;
3664 }
3665
3666 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3667 {
3668   int real_chunk_size = 0;
3669
3670   li = *level;          // copy level data into temporary buffer
3671
3672   while (!checkEndOfFile(file))
3673   {
3674     int element = getMappedElement(getFile16BitBE(file));
3675
3676     real_chunk_size += 2;
3677     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3678                                             element, element);
3679     if (real_chunk_size >= chunk_size)
3680       break;
3681   }
3682
3683   *level = li;          // copy temporary buffer back to level data
3684
3685   return real_chunk_size;
3686 }
3687
3688 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3689 {
3690   int element = getMappedElement(getFile16BitBE(file));
3691   int envelope_nr = element - EL_ENVELOPE_1;
3692   int real_chunk_size = 2;
3693
3694   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3695
3696   while (!checkEndOfFile(file))
3697   {
3698     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3699                                             -1, element);
3700
3701     if (real_chunk_size >= chunk_size)
3702       break;
3703   }
3704
3705   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3706
3707   return real_chunk_size;
3708 }
3709
3710 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3711 {
3712   int element = getMappedElement(getFile16BitBE(file));
3713   int real_chunk_size = 2;
3714   struct ElementInfo *ei = &element_info[element];
3715   int i;
3716
3717   xx_ei = *ei;          // copy element data into temporary buffer
3718
3719   xx_ei.num_change_pages = -1;
3720
3721   while (!checkEndOfFile(file))
3722   {
3723     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3724                                             -1, element);
3725     if (xx_ei.num_change_pages != -1)
3726       break;
3727
3728     if (real_chunk_size >= chunk_size)
3729       break;
3730   }
3731
3732   *ei = xx_ei;
3733
3734   if (ei->num_change_pages == -1)
3735   {
3736     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3737          EL_NAME(element));
3738
3739     ei->num_change_pages = 1;
3740
3741     setElementChangePages(ei, 1);
3742     setElementChangeInfoToDefaults(ei->change);
3743
3744     return real_chunk_size;
3745   }
3746
3747   // initialize number of change pages stored for this custom element
3748   setElementChangePages(ei, ei->num_change_pages);
3749   for (i = 0; i < ei->num_change_pages; i++)
3750     setElementChangeInfoToDefaults(&ei->change_page[i]);
3751
3752   // start with reading properties for the first change page
3753   xx_current_change_page = 0;
3754
3755   while (!checkEndOfFile(file))
3756   {
3757     // level file might contain invalid change page number
3758     if (xx_current_change_page >= ei->num_change_pages)
3759       break;
3760
3761     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3762
3763     xx_change = *change;        // copy change data into temporary buffer
3764
3765     resetEventBits();           // reset bits; change page might have changed
3766
3767     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3768                                             -1, element);
3769
3770     *change = xx_change;
3771
3772     setEventFlagsFromEventBits(change);
3773
3774     if (real_chunk_size >= chunk_size)
3775       break;
3776   }
3777
3778   level->file_has_custom_elements = TRUE;
3779
3780   return real_chunk_size;
3781 }
3782
3783 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3784 {
3785   int element = getMappedElement(getFile16BitBE(file));
3786   int real_chunk_size = 2;
3787   struct ElementInfo *ei = &element_info[element];
3788   struct ElementGroupInfo *group = ei->group;
3789
3790   if (group == NULL)
3791     return -1;
3792
3793   xx_ei = *ei;          // copy element data into temporary buffer
3794   xx_group = *group;    // copy group data into temporary buffer
3795
3796   while (!checkEndOfFile(file))
3797   {
3798     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3799                                             -1, element);
3800
3801     if (real_chunk_size >= chunk_size)
3802       break;
3803   }
3804
3805   *ei = xx_ei;
3806   *group = xx_group;
3807
3808   level->file_has_custom_elements = TRUE;
3809
3810   return real_chunk_size;
3811 }
3812
3813 static int LoadLevel_EMPX(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
3819   xx_ei = *ei;          // copy element data into temporary buffer
3820
3821   while (!checkEndOfFile(file))
3822   {
3823     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3824                                             -1, element);
3825
3826     if (real_chunk_size >= chunk_size)
3827       break;
3828   }
3829
3830   *ei = xx_ei;
3831
3832   level->file_has_custom_elements = TRUE;
3833
3834   return real_chunk_size;
3835 }
3836
3837 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3838                                       struct LevelFileInfo *level_file_info,
3839                                       boolean level_info_only)
3840 {
3841   char *filename = level_file_info->filename;
3842   char cookie[MAX_LINE_LEN];
3843   char chunk_name[CHUNK_ID_LEN + 1];
3844   int chunk_size;
3845   File *file;
3846
3847   if (!(file = openFile(filename, MODE_READ)))
3848   {
3849     level->no_valid_file = TRUE;
3850     level->no_level_file = TRUE;
3851
3852     if (level_info_only)
3853       return;
3854
3855     Warn("cannot read level '%s' -- using empty level", filename);
3856
3857     if (!setup.editor.use_template_for_new_levels)
3858       return;
3859
3860     // if level file not found, try to initialize level data from template
3861     filename = getGlobalLevelTemplateFilename();
3862
3863     if (!(file = openFile(filename, MODE_READ)))
3864       return;
3865
3866     // default: for empty levels, use level template for custom elements
3867     level->use_custom_template = TRUE;
3868
3869     level->no_valid_file = FALSE;
3870   }
3871
3872   getFileChunkBE(file, chunk_name, NULL);
3873   if (strEqual(chunk_name, "RND1"))
3874   {
3875     getFile32BitBE(file);               // not used
3876
3877     getFileChunkBE(file, chunk_name, NULL);
3878     if (!strEqual(chunk_name, "CAVE"))
3879     {
3880       level->no_valid_file = TRUE;
3881
3882       Warn("unknown format of level file '%s'", filename);
3883
3884       closeFile(file);
3885
3886       return;
3887     }
3888   }
3889   else  // check for pre-2.0 file format with cookie string
3890   {
3891     strcpy(cookie, chunk_name);
3892     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3893       cookie[4] = '\0';
3894     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3895       cookie[strlen(cookie) - 1] = '\0';
3896
3897     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3898     {
3899       level->no_valid_file = TRUE;
3900
3901       Warn("unknown format of level file '%s'", filename);
3902
3903       closeFile(file);
3904
3905       return;
3906     }
3907
3908     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3909     {
3910       level->no_valid_file = TRUE;
3911
3912       Warn("unsupported version of level file '%s'", filename);
3913
3914       closeFile(file);
3915
3916       return;
3917     }
3918
3919     // pre-2.0 level files have no game version, so use file version here
3920     level->game_version = level->file_version;
3921   }
3922
3923   if (level->file_version < FILE_VERSION_1_2)
3924   {
3925     // level files from versions before 1.2.0 without chunk structure
3926     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3927     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3928   }
3929   else
3930   {
3931     static struct
3932     {
3933       char *name;
3934       int size;
3935       int (*loader)(File *, int, struct LevelInfo *);
3936     }
3937     chunk_info[] =
3938     {
3939       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3940       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3941       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3942       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3943       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3944       { "INFO", -1,                     LoadLevel_INFO },
3945       { "BODY", -1,                     LoadLevel_BODY },
3946       { "CONT", -1,                     LoadLevel_CONT },
3947       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3948       { "CNT3", -1,                     LoadLevel_CNT3 },
3949       { "CUS1", -1,                     LoadLevel_CUS1 },
3950       { "CUS2", -1,                     LoadLevel_CUS2 },
3951       { "CUS3", -1,                     LoadLevel_CUS3 },
3952       { "CUS4", -1,                     LoadLevel_CUS4 },
3953       { "GRP1", -1,                     LoadLevel_GRP1 },
3954       { "CONF", -1,                     LoadLevel_CONF },
3955       { "ELEM", -1,                     LoadLevel_ELEM },
3956       { "NOTE", -1,                     LoadLevel_NOTE },
3957       { "CUSX", -1,                     LoadLevel_CUSX },
3958       { "GRPX", -1,                     LoadLevel_GRPX },
3959       { "EMPX", -1,                     LoadLevel_EMPX },
3960
3961       {  NULL,  0,                      NULL }
3962     };
3963
3964     while (getFileChunkBE(file, chunk_name, &chunk_size))
3965     {
3966       int i = 0;
3967
3968       while (chunk_info[i].name != NULL &&
3969              !strEqual(chunk_name, chunk_info[i].name))
3970         i++;
3971
3972       if (chunk_info[i].name == NULL)
3973       {
3974         Warn("unknown chunk '%s' in level file '%s'",
3975              chunk_name, filename);
3976
3977         ReadUnusedBytesFromFile(file, chunk_size);
3978       }
3979       else if (chunk_info[i].size != -1 &&
3980                chunk_info[i].size != chunk_size)
3981       {
3982         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3983              chunk_size, chunk_name, filename);
3984
3985         ReadUnusedBytesFromFile(file, chunk_size);
3986       }
3987       else
3988       {
3989         // call function to load this level chunk
3990         int chunk_size_expected =
3991           (chunk_info[i].loader)(file, chunk_size, level);
3992
3993         if (chunk_size_expected < 0)
3994         {
3995           Warn("error reading chunk '%s' in level file '%s'",
3996                chunk_name, filename);
3997
3998           break;
3999         }
4000
4001         // the size of some chunks cannot be checked before reading other
4002         // chunks first (like "HEAD" and "BODY") that contain some header
4003         // information, so check them here
4004         if (chunk_size_expected != chunk_size)
4005         {
4006           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4007                chunk_size, chunk_name, filename);
4008
4009           break;
4010         }
4011       }
4012     }
4013   }
4014
4015   closeFile(file);
4016 }
4017
4018
4019 // ----------------------------------------------------------------------------
4020 // functions for loading BD level
4021 // ----------------------------------------------------------------------------
4022
4023 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4024 {
4025   struct LevelInfo_BD *level_bd = level->native_bd_level;
4026   GdCave *cave = NULL;  // will be changed below
4027   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4028   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4029   int x, y;
4030
4031   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4032
4033   // cave and map newly allocated when set to defaults above
4034   cave = level_bd->cave;
4035
4036   // level type
4037   cave->intermission                    = level->bd_intermission;
4038
4039   // level settings
4040   cave->level_time[0]                   = level->time;
4041   cave->level_diamonds[0]               = level->gems_needed;
4042
4043   // game timing
4044   cave->scheduling                      = level->bd_scheduling_type;
4045   cave->pal_timing                      = level->bd_pal_timing;
4046   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4047   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4048   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4049   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4050
4051   // scores
4052   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4053   cave->diamond_value                   = level->score[SC_EMERALD];
4054   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4055
4056   // compatibility settings
4057   cave->lineshift                       = level->bd_line_shifting_borders;
4058   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4059   cave->short_explosions                = level->bd_short_explosions;
4060   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4061
4062   // player properties
4063   cave->diagonal_movements              = level->bd_diagonal_movements;
4064   cave->active_is_first_found           = level->bd_topmost_player_active;
4065   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4066   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4067   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4068   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4069
4070   // element properties
4071   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4072   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4073   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4074   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4075   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4076   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4077   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4078   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4079   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4080
4081   cave->magic_diamond_to                = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4082   cave->magic_stone_to                  = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4083   cave->magic_mega_stone_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4084   cave->magic_nut_to                    = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4085   cave->magic_nitro_pack_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4086   cave->magic_flying_diamond_to         = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4087   cave->magic_flying_stone_to           = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4088
4089   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4090   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4091   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4092   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4093   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4094   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4095   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4096   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4097   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4098   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4099   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4100
4101   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4102   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4103   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4104   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4105   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4106   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4107
4108   cave->slime_predictable               = level->bd_slime_is_predictable;
4109   cave->slime_correct_random            = level->bd_slime_correct_random;
4110   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4111   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4112   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4113   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4114
4115   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4116   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4117   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4118
4119   cave->biter_delay_frame               = level->bd_biter_move_delay;
4120   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4121
4122   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4123
4124   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4125
4126   cave->replicators_active              = level->bd_replicators_active;
4127   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4128
4129   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4130   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4131
4132   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4133
4134   cave->nut_turns_to_when_crushed       = map_element_RND_to_BD_cave(level->bd_nut_content);
4135
4136   // level name
4137   strncpy(cave->name, level->name, sizeof(GdString));
4138   cave->name[sizeof(GdString) - 1] = '\0';
4139
4140   // playfield elements
4141   for (x = 0; x < cave->w; x++)
4142     for (y = 0; y < cave->h; y++)
4143       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4144 }
4145
4146 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4147 {
4148   struct LevelInfo_BD *level_bd = level->native_bd_level;
4149   GdCave *cave = level_bd->cave;
4150   int bd_level_nr = level_bd->level_nr;
4151   int x, y;
4152
4153   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4154   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4155
4156   // level type
4157   level->bd_intermission                = cave->intermission;
4158
4159   // level settings
4160   level->time                           = cave->level_time[bd_level_nr];
4161   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4162
4163   // game timing
4164   level->bd_scheduling_type             = cave->scheduling;
4165   level->bd_pal_timing                  = cave->pal_timing;
4166   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4167   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4168   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4169   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4170
4171   // scores
4172   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4173   level->score[SC_EMERALD]              = cave->diamond_value;
4174   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4175
4176   // compatibility settings
4177   level->bd_line_shifting_borders       = cave->lineshift;
4178   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4179   level->bd_short_explosions            = cave->short_explosions;
4180   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4181
4182   // player properties
4183   level->bd_diagonal_movements          = cave->diagonal_movements;
4184   level->bd_topmost_player_active       = cave->active_is_first_found;
4185   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4186   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4187   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4188   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4189
4190   // element properties
4191   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4192   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4193   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4194   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4195   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4196   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4197   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4198   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4199   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4200
4201   level->bd_magic_wall_diamond_to       = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4202   level->bd_magic_wall_rock_to          = map_element_BD_to_RND_cave(cave->magic_stone_to);
4203   level->bd_magic_wall_mega_rock_to     = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4204   level->bd_magic_wall_nut_to           = map_element_BD_to_RND_cave(cave->magic_nut_to);
4205   level->bd_magic_wall_nitro_pack_to    = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4206   level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4207   level->bd_magic_wall_flying_rock_to   = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4208
4209   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4210   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4211   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4212   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4213   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4214   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4215   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4216   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4217   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4218   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4219   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4220
4221   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4222   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4223   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4224   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4225   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4226   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4227
4228   level->bd_slime_is_predictable        = cave->slime_predictable;
4229   level->bd_slime_correct_random        = cave->slime_correct_random;
4230   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4231   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4232   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4233   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4234
4235   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4236   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4237   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4238
4239   level->bd_biter_move_delay            = cave->biter_delay_frame;
4240   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4241
4242   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4243
4244   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4245
4246   level->bd_replicators_active          = cave->replicators_active;
4247   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4248
4249   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4250   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4251
4252   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4253
4254   level->bd_nut_content                 = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4255
4256   // level name
4257   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4258
4259   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4260   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4261
4262   // playfield elements
4263   for (x = 0; x < level->fieldx; x++)
4264     for (y = 0; y < level->fieldy; y++)
4265       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4266
4267   checked_free(cave_name);
4268 }
4269
4270 static void setTapeInfoToDefaults(void);
4271
4272 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4273 {
4274   struct LevelInfo_BD *level_bd = level->native_bd_level;
4275   GdCave *cave = level_bd->cave;
4276   GdReplay *replay = level_bd->replay;
4277   int i;
4278
4279   if (replay == NULL)
4280     return;
4281
4282   // always start with reliable default values
4283   setTapeInfoToDefaults();
4284
4285   tape.level_nr = level_nr;             // (currently not used)
4286   tape.random_seed = replay->seed;
4287
4288   TapeSetDateFromIsoDateString(replay->date);
4289
4290   tape.counter = 0;
4291   tape.pos[tape.counter].delay = 0;
4292
4293   tape.bd_replay = TRUE;
4294
4295   // all time calculations only used to display approximate tape time
4296   int cave_speed = cave->speed;
4297   int milliseconds_game = 0;
4298   int milliseconds_elapsed = 20;
4299
4300   for (i = 0; i < replay->movements->len; i++)
4301   {
4302     int replay_action = replay->movements->data[i];
4303     int tape_action = map_action_BD_to_RND(replay_action);
4304     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4305     boolean success = 0;
4306
4307     while (1)
4308     {
4309       success = TapeAddAction(action);
4310
4311       milliseconds_game += milliseconds_elapsed;
4312
4313       if (milliseconds_game >= cave_speed)
4314       {
4315         milliseconds_game -= cave_speed;
4316
4317         break;
4318       }
4319     }
4320
4321     tape.counter++;
4322     tape.pos[tape.counter].delay = 0;
4323     tape.pos[tape.counter].action[0] = 0;
4324
4325     if (!success)
4326     {
4327       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4328
4329       break;
4330     }
4331   }
4332
4333   TapeHaltRecording();
4334 }
4335
4336
4337 // ----------------------------------------------------------------------------
4338 // functions for loading EM level
4339 // ----------------------------------------------------------------------------
4340
4341 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4342 {
4343   static int ball_xy[8][2] =
4344   {
4345     { 0, 0 },
4346     { 1, 0 },
4347     { 2, 0 },
4348     { 0, 1 },
4349     { 2, 1 },
4350     { 0, 2 },
4351     { 1, 2 },
4352     { 2, 2 },
4353   };
4354   struct LevelInfo_EM *level_em = level->native_em_level;
4355   struct CAVE *cav = level_em->cav;
4356   int i, j, x, y;
4357
4358   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4359   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4360
4361   cav->time_seconds     = level->time;
4362   cav->gems_needed      = level->gems_needed;
4363
4364   cav->emerald_score    = level->score[SC_EMERALD];
4365   cav->diamond_score    = level->score[SC_DIAMOND];
4366   cav->alien_score      = level->score[SC_ROBOT];
4367   cav->tank_score       = level->score[SC_SPACESHIP];
4368   cav->bug_score        = level->score[SC_BUG];
4369   cav->eater_score      = level->score[SC_YAMYAM];
4370   cav->nut_score        = level->score[SC_NUT];
4371   cav->dynamite_score   = level->score[SC_DYNAMITE];
4372   cav->key_score        = level->score[SC_KEY];
4373   cav->exit_score       = level->score[SC_TIME_BONUS];
4374
4375   cav->num_eater_arrays = level->num_yamyam_contents;
4376
4377   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4378     for (y = 0; y < 3; y++)
4379       for (x = 0; x < 3; x++)
4380         cav->eater_array[i][y * 3 + x] =
4381           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4382
4383   cav->amoeba_time              = level->amoeba_speed;
4384   cav->wonderwall_time          = level->time_magic_wall;
4385   cav->wheel_time               = level->time_wheel;
4386
4387   cav->android_move_time        = level->android_move_time;
4388   cav->android_clone_time       = level->android_clone_time;
4389   cav->ball_random              = level->ball_random;
4390   cav->ball_active              = level->ball_active_initial;
4391   cav->ball_time                = level->ball_time;
4392   cav->num_ball_arrays          = level->num_ball_contents;
4393
4394   cav->lenses_score             = level->lenses_score;
4395   cav->magnify_score            = level->magnify_score;
4396   cav->slurp_score              = level->slurp_score;
4397
4398   cav->lenses_time              = level->lenses_time;
4399   cav->magnify_time             = level->magnify_time;
4400
4401   cav->wind_time = 9999;
4402   cav->wind_direction =
4403     map_direction_RND_to_EM(level->wind_direction_initial);
4404
4405   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4406     for (j = 0; j < 8; j++)
4407       cav->ball_array[i][j] =
4408         map_element_RND_to_EM_cave(level->ball_content[i].
4409                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4410
4411   map_android_clone_elements_RND_to_EM(level);
4412
4413   // first fill the complete playfield with the empty space element
4414   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4415     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4416       cav->cave[x][y] = Cblank;
4417
4418   // then copy the real level contents from level file into the playfield
4419   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4420   {
4421     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4422
4423     if (level->field[x][y] == EL_AMOEBA_DEAD)
4424       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4425
4426     cav->cave[x][y] = new_element;
4427   }
4428
4429   for (i = 0; i < MAX_PLAYERS; i++)
4430   {
4431     cav->player_x[i] = -1;
4432     cav->player_y[i] = -1;
4433   }
4434
4435   // initialize player positions and delete players from the playfield
4436   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4437   {
4438     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4439     {
4440       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4441
4442       cav->player_x[player_nr] = x;
4443       cav->player_y[player_nr] = y;
4444
4445       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4446     }
4447   }
4448 }
4449
4450 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4451 {
4452   static int ball_xy[8][2] =
4453   {
4454     { 0, 0 },
4455     { 1, 0 },
4456     { 2, 0 },
4457     { 0, 1 },
4458     { 2, 1 },
4459     { 0, 2 },
4460     { 1, 2 },
4461     { 2, 2 },
4462   };
4463   struct LevelInfo_EM *level_em = level->native_em_level;
4464   struct CAVE *cav = level_em->cav;
4465   int i, j, x, y;
4466
4467   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4468   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4469
4470   level->time        = cav->time_seconds;
4471   level->gems_needed = cav->gems_needed;
4472
4473   sprintf(level->name, "Level %d", level->file_info.nr);
4474
4475   level->score[SC_EMERALD]      = cav->emerald_score;
4476   level->score[SC_DIAMOND]      = cav->diamond_score;
4477   level->score[SC_ROBOT]        = cav->alien_score;
4478   level->score[SC_SPACESHIP]    = cav->tank_score;
4479   level->score[SC_BUG]          = cav->bug_score;
4480   level->score[SC_YAMYAM]       = cav->eater_score;
4481   level->score[SC_NUT]          = cav->nut_score;
4482   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4483   level->score[SC_KEY]          = cav->key_score;
4484   level->score[SC_TIME_BONUS]   = cav->exit_score;
4485
4486   level->num_yamyam_contents    = cav->num_eater_arrays;
4487
4488   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4489     for (y = 0; y < 3; y++)
4490       for (x = 0; x < 3; x++)
4491         level->yamyam_content[i].e[x][y] =
4492           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4493
4494   level->amoeba_speed           = cav->amoeba_time;
4495   level->time_magic_wall        = cav->wonderwall_time;
4496   level->time_wheel             = cav->wheel_time;
4497
4498   level->android_move_time      = cav->android_move_time;
4499   level->android_clone_time     = cav->android_clone_time;
4500   level->ball_random            = cav->ball_random;
4501   level->ball_active_initial    = cav->ball_active;
4502   level->ball_time              = cav->ball_time;
4503   level->num_ball_contents      = cav->num_ball_arrays;
4504
4505   level->lenses_score           = cav->lenses_score;
4506   level->magnify_score          = cav->magnify_score;
4507   level->slurp_score            = cav->slurp_score;
4508
4509   level->lenses_time            = cav->lenses_time;
4510   level->magnify_time           = cav->magnify_time;
4511
4512   level->wind_direction_initial =
4513     map_direction_EM_to_RND(cav->wind_direction);
4514
4515   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4516     for (j = 0; j < 8; j++)
4517       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4518         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4519
4520   map_android_clone_elements_EM_to_RND(level);
4521
4522   // convert the playfield (some elements need special treatment)
4523   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4524   {
4525     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4526
4527     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4528       new_element = EL_AMOEBA_DEAD;
4529
4530     level->field[x][y] = new_element;
4531   }
4532
4533   for (i = 0; i < MAX_PLAYERS; i++)
4534   {
4535     // in case of all players set to the same field, use the first player
4536     int nr = MAX_PLAYERS - i - 1;
4537     int jx = cav->player_x[nr];
4538     int jy = cav->player_y[nr];
4539
4540     if (jx != -1 && jy != -1)
4541       level->field[jx][jy] = EL_PLAYER_1 + nr;
4542   }
4543
4544   // time score is counted for each 10 seconds left in Emerald Mine levels
4545   level->time_score_base = 10;
4546 }
4547
4548
4549 // ----------------------------------------------------------------------------
4550 // functions for loading SP level
4551 // ----------------------------------------------------------------------------
4552
4553 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4554 {
4555   struct LevelInfo_SP *level_sp = level->native_sp_level;
4556   LevelInfoType *header = &level_sp->header;
4557   int i, x, y;
4558
4559   level_sp->width  = level->fieldx;
4560   level_sp->height = level->fieldy;
4561
4562   for (x = 0; x < level->fieldx; x++)
4563     for (y = 0; y < level->fieldy; y++)
4564       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4565
4566   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4567
4568   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4569     header->LevelTitle[i] = level->name[i];
4570   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4571
4572   header->InfotronsNeeded = level->gems_needed;
4573
4574   header->SpecialPortCount = 0;
4575
4576   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4577   {
4578     boolean gravity_port_found = FALSE;
4579     boolean gravity_port_valid = FALSE;
4580     int gravity_port_flag;
4581     int gravity_port_base_element;
4582     int element = level->field[x][y];
4583
4584     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4585         element <= EL_SP_GRAVITY_ON_PORT_UP)
4586     {
4587       gravity_port_found = TRUE;
4588       gravity_port_valid = TRUE;
4589       gravity_port_flag = 1;
4590       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4591     }
4592     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4593              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4594     {
4595       gravity_port_found = TRUE;
4596       gravity_port_valid = TRUE;
4597       gravity_port_flag = 0;
4598       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4599     }
4600     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4601              element <= EL_SP_GRAVITY_PORT_UP)
4602     {
4603       // change R'n'D style gravity inverting special port to normal port
4604       // (there are no gravity inverting ports in native Supaplex engine)
4605
4606       gravity_port_found = TRUE;
4607       gravity_port_valid = FALSE;
4608       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4609     }
4610
4611     if (gravity_port_found)
4612     {
4613       if (gravity_port_valid &&
4614           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4615       {
4616         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4617
4618         port->PortLocation = (y * level->fieldx + x) * 2;
4619         port->Gravity = gravity_port_flag;
4620
4621         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4622
4623         header->SpecialPortCount++;
4624       }
4625       else
4626       {
4627         // change special gravity port to normal port
4628
4629         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4630       }
4631
4632       level_sp->playfield[x][y] = element - EL_SP_START;
4633     }
4634   }
4635 }
4636
4637 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4638 {
4639   struct LevelInfo_SP *level_sp = level->native_sp_level;
4640   LevelInfoType *header = &level_sp->header;
4641   boolean num_invalid_elements = 0;
4642   int i, j, x, y;
4643
4644   level->fieldx = level_sp->width;
4645   level->fieldy = level_sp->height;
4646
4647   for (x = 0; x < level->fieldx; x++)
4648   {
4649     for (y = 0; y < level->fieldy; y++)
4650     {
4651       int element_old = level_sp->playfield[x][y];
4652       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4653
4654       if (element_new == EL_UNKNOWN)
4655       {
4656         num_invalid_elements++;
4657
4658         Debug("level:native:SP", "invalid element %d at position %d, %d",
4659               element_old, x, y);
4660       }
4661
4662       level->field[x][y] = element_new;
4663     }
4664   }
4665
4666   if (num_invalid_elements > 0)
4667     Warn("found %d invalid elements%s", num_invalid_elements,
4668          (!options.debug ? " (use '--debug' for more details)" : ""));
4669
4670   for (i = 0; i < MAX_PLAYERS; i++)
4671     level->initial_player_gravity[i] =
4672       (header->InitialGravity == 1 ? TRUE : FALSE);
4673
4674   // skip leading spaces
4675   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4676     if (header->LevelTitle[i] != ' ')
4677       break;
4678
4679   // copy level title
4680   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4681     level->name[j] = header->LevelTitle[i];
4682   level->name[j] = '\0';
4683
4684   // cut trailing spaces
4685   for (; j > 0; j--)
4686     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4687       level->name[j - 1] = '\0';
4688
4689   level->gems_needed = header->InfotronsNeeded;
4690
4691   for (i = 0; i < header->SpecialPortCount; i++)
4692   {
4693     SpecialPortType *port = &header->SpecialPort[i];
4694     int port_location = port->PortLocation;
4695     int gravity = port->Gravity;
4696     int port_x, port_y, port_element;
4697
4698     port_x = (port_location / 2) % level->fieldx;
4699     port_y = (port_location / 2) / level->fieldx;
4700
4701     if (port_x < 0 || port_x >= level->fieldx ||
4702         port_y < 0 || port_y >= level->fieldy)
4703     {
4704       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4705
4706       continue;
4707     }
4708
4709     port_element = level->field[port_x][port_y];
4710
4711     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4712         port_element > EL_SP_GRAVITY_PORT_UP)
4713     {
4714       Warn("no special port at position (%d, %d)", port_x, port_y);
4715
4716       continue;
4717     }
4718
4719     // change previous (wrong) gravity inverting special port to either
4720     // gravity enabling special port or gravity disabling special port
4721     level->field[port_x][port_y] +=
4722       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4723        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4724   }
4725
4726   // change special gravity ports without database entries to normal ports
4727   for (x = 0; x < level->fieldx; x++)
4728     for (y = 0; y < level->fieldy; y++)
4729       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4730           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4731         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4732
4733   level->time = 0;                      // no time limit
4734   level->amoeba_speed = 0;
4735   level->time_magic_wall = 0;
4736   level->time_wheel = 0;
4737   level->amoeba_content = EL_EMPTY;
4738
4739   // original Supaplex does not use score values -- rate by playing time
4740   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4741     level->score[i] = 0;
4742
4743   level->rate_time_over_score = TRUE;
4744
4745   // there are no yamyams in supaplex levels
4746   for (i = 0; i < level->num_yamyam_contents; i++)
4747     for (x = 0; x < 3; x++)
4748       for (y = 0; y < 3; y++)
4749         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4750 }
4751
4752 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4753 {
4754   struct LevelInfo_SP *level_sp = level->native_sp_level;
4755   struct DemoInfo_SP *demo = &level_sp->demo;
4756   int i, j;
4757
4758   // always start with reliable default values
4759   demo->is_available = FALSE;
4760   demo->length = 0;
4761
4762   if (TAPE_IS_EMPTY(tape))
4763     return;
4764
4765   demo->level_nr = tape.level_nr;       // (currently not used)
4766
4767   level_sp->header.DemoRandomSeed = tape.random_seed;
4768
4769   demo->length = 0;
4770
4771   for (i = 0; i < tape.length; i++)
4772   {
4773     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4774     int demo_repeat = tape.pos[i].delay;
4775     int demo_entries = (demo_repeat + 15) / 16;
4776
4777     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4778     {
4779       Warn("tape truncated: size exceeds maximum SP demo size %d",
4780            SP_MAX_TAPE_LEN);
4781
4782       break;
4783     }
4784
4785     for (j = 0; j < demo_repeat / 16; j++)
4786       demo->data[demo->length++] = 0xf0 | demo_action;
4787
4788     if (demo_repeat % 16)
4789       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4790   }
4791
4792   demo->is_available = TRUE;
4793 }
4794
4795 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4796 {
4797   struct LevelInfo_SP *level_sp = level->native_sp_level;
4798   struct DemoInfo_SP *demo = &level_sp->demo;
4799   char *filename = level->file_info.filename;
4800   int i;
4801
4802   // always start with reliable default values
4803   setTapeInfoToDefaults();
4804
4805   if (!demo->is_available)
4806     return;
4807
4808   tape.level_nr = demo->level_nr;       // (currently not used)
4809   tape.random_seed = level_sp->header.DemoRandomSeed;
4810
4811   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4812
4813   tape.counter = 0;
4814   tape.pos[tape.counter].delay = 0;
4815
4816   for (i = 0; i < demo->length; i++)
4817   {
4818     int demo_action = demo->data[i] & 0x0f;
4819     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4820     int tape_action = map_key_SP_to_RND(demo_action);
4821     int tape_repeat = demo_repeat + 1;
4822     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4823     boolean success = 0;
4824     int j;
4825
4826     for (j = 0; j < tape_repeat; j++)
4827       success = TapeAddAction(action);
4828
4829     if (!success)
4830     {
4831       Warn("SP demo truncated: size exceeds maximum tape size %d",
4832            MAX_TAPE_LEN);
4833
4834       break;
4835     }
4836   }
4837
4838   TapeHaltRecording();
4839 }
4840
4841
4842 // ----------------------------------------------------------------------------
4843 // functions for loading MM level
4844 // ----------------------------------------------------------------------------
4845
4846 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4847 {
4848   struct LevelInfo_MM *level_mm = level->native_mm_level;
4849   int i, x, y;
4850
4851   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4852   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4853
4854   level_mm->time = level->time;
4855   level_mm->kettles_needed = level->gems_needed;
4856   level_mm->auto_count_kettles = level->auto_count_gems;
4857
4858   level_mm->mm_laser_red   = level->mm_laser_red;
4859   level_mm->mm_laser_green = level->mm_laser_green;
4860   level_mm->mm_laser_blue  = level->mm_laser_blue;
4861
4862   level_mm->df_laser_red   = level->df_laser_red;
4863   level_mm->df_laser_green = level->df_laser_green;
4864   level_mm->df_laser_blue  = level->df_laser_blue;
4865
4866   strcpy(level_mm->name, level->name);
4867   strcpy(level_mm->author, level->author);
4868
4869   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4870   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4871   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4872   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4873   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4874
4875   level_mm->amoeba_speed = level->amoeba_speed;
4876   level_mm->time_fuse    = level->mm_time_fuse;
4877   level_mm->time_bomb    = level->mm_time_bomb;
4878   level_mm->time_ball    = level->mm_time_ball;
4879   level_mm->time_block   = level->mm_time_block;
4880
4881   level_mm->num_ball_contents = level->num_mm_ball_contents;
4882   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4883   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4884   level_mm->explode_ball = level->explode_mm_ball;
4885
4886   for (i = 0; i < level->num_mm_ball_contents; i++)
4887     level_mm->ball_content[i] =
4888       map_element_RND_to_MM(level->mm_ball_content[i]);
4889
4890   for (x = 0; x < level->fieldx; x++)
4891     for (y = 0; y < level->fieldy; y++)
4892       Ur[x][y] =
4893         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4894 }
4895
4896 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4897 {
4898   struct LevelInfo_MM *level_mm = level->native_mm_level;
4899   int i, x, y;
4900
4901   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4902   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4903
4904   level->time = level_mm->time;
4905   level->gems_needed = level_mm->kettles_needed;
4906   level->auto_count_gems = level_mm->auto_count_kettles;
4907
4908   level->mm_laser_red   = level_mm->mm_laser_red;
4909   level->mm_laser_green = level_mm->mm_laser_green;
4910   level->mm_laser_blue  = level_mm->mm_laser_blue;
4911
4912   level->df_laser_red   = level_mm->df_laser_red;
4913   level->df_laser_green = level_mm->df_laser_green;
4914   level->df_laser_blue  = level_mm->df_laser_blue;
4915
4916   strcpy(level->name, level_mm->name);
4917
4918   // only overwrite author from 'levelinfo.conf' if author defined in level
4919   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4920     strcpy(level->author, level_mm->author);
4921
4922   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4923   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4924   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4925   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4926   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4927
4928   level->amoeba_speed  = level_mm->amoeba_speed;
4929   level->mm_time_fuse  = level_mm->time_fuse;
4930   level->mm_time_bomb  = level_mm->time_bomb;
4931   level->mm_time_ball  = level_mm->time_ball;
4932   level->mm_time_block = level_mm->time_block;
4933
4934   level->num_mm_ball_contents = level_mm->num_ball_contents;
4935   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4936   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4937   level->explode_mm_ball = level_mm->explode_ball;
4938
4939   for (i = 0; i < level->num_mm_ball_contents; i++)
4940     level->mm_ball_content[i] =
4941       map_element_MM_to_RND(level_mm->ball_content[i]);
4942
4943   for (x = 0; x < level->fieldx; x++)
4944     for (y = 0; y < level->fieldy; y++)
4945       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4946 }
4947
4948
4949 // ----------------------------------------------------------------------------
4950 // functions for loading DC level
4951 // ----------------------------------------------------------------------------
4952
4953 #define DC_LEVEL_HEADER_SIZE            344
4954
4955 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4956                                         boolean init)
4957 {
4958   static int last_data_encoded;
4959   static int offset1;
4960   static int offset2;
4961   int diff;
4962   int diff_hi, diff_lo;
4963   int data_hi, data_lo;
4964   unsigned short data_decoded;
4965
4966   if (init)
4967   {
4968     last_data_encoded = 0;
4969     offset1 = -1;
4970     offset2 = 0;
4971
4972     return 0;
4973   }
4974
4975   diff = data_encoded - last_data_encoded;
4976   diff_hi = diff & ~0xff;
4977   diff_lo = diff &  0xff;
4978
4979   offset2 += diff_lo;
4980
4981   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4982   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4983   data_hi = data_hi & 0xff00;
4984
4985   data_decoded = data_hi | data_lo;
4986
4987   last_data_encoded = data_encoded;
4988
4989   offset1 = (offset1 + 1) % 31;
4990   offset2 = offset2 & 0xff;
4991
4992   return data_decoded;
4993 }
4994
4995 static int getMappedElement_DC(int element)
4996 {
4997   switch (element)
4998   {
4999     case 0x0000:
5000       element = EL_ROCK;
5001       break;
5002
5003       // 0x0117 - 0x036e: (?)
5004       // EL_DIAMOND
5005
5006       // 0x042d - 0x0684: (?)
5007       // EL_EMERALD
5008
5009     case 0x06f1:
5010       element = EL_NUT;
5011       break;
5012
5013     case 0x074c:
5014       element = EL_BOMB;
5015       break;
5016
5017     case 0x07a4:
5018       element = EL_PEARL;
5019       break;
5020
5021     case 0x0823:
5022       element = EL_CRYSTAL;
5023       break;
5024
5025     case 0x0e77:        // quicksand (boulder)
5026       element = EL_QUICKSAND_FAST_FULL;
5027       break;
5028
5029     case 0x0e99:        // slow quicksand (boulder)
5030       element = EL_QUICKSAND_FULL;
5031       break;
5032
5033     case 0x0ed2:
5034       element = EL_EM_EXIT_OPEN;
5035       break;
5036
5037     case 0x0ee3:
5038       element = EL_EM_EXIT_CLOSED;
5039       break;
5040
5041     case 0x0eeb:
5042       element = EL_EM_STEEL_EXIT_OPEN;
5043       break;
5044
5045     case 0x0efc:
5046       element = EL_EM_STEEL_EXIT_CLOSED;
5047       break;
5048
5049     case 0x0f4f:        // dynamite (lit 1)
5050       element = EL_EM_DYNAMITE_ACTIVE;
5051       break;
5052
5053     case 0x0f57:        // dynamite (lit 2)
5054       element = EL_EM_DYNAMITE_ACTIVE;
5055       break;
5056
5057     case 0x0f5f:        // dynamite (lit 3)
5058       element = EL_EM_DYNAMITE_ACTIVE;
5059       break;
5060
5061     case 0x0f67:        // dynamite (lit 4)
5062       element = EL_EM_DYNAMITE_ACTIVE;
5063       break;
5064
5065     case 0x0f81:
5066     case 0x0f82:
5067     case 0x0f83:
5068     case 0x0f84:
5069       element = EL_AMOEBA_WET;
5070       break;
5071
5072     case 0x0f85:
5073       element = EL_AMOEBA_DROP;
5074       break;
5075
5076     case 0x0fb9:
5077       element = EL_DC_MAGIC_WALL;
5078       break;
5079
5080     case 0x0fd0:
5081       element = EL_SPACESHIP_UP;
5082       break;
5083
5084     case 0x0fd9:
5085       element = EL_SPACESHIP_DOWN;
5086       break;
5087
5088     case 0x0ff1:
5089       element = EL_SPACESHIP_LEFT;
5090       break;
5091
5092     case 0x0ff9:
5093       element = EL_SPACESHIP_RIGHT;
5094       break;
5095
5096     case 0x1057:
5097       element = EL_BUG_UP;
5098       break;
5099
5100     case 0x1060:
5101       element = EL_BUG_DOWN;
5102       break;
5103
5104     case 0x1078:
5105       element = EL_BUG_LEFT;
5106       break;
5107
5108     case 0x1080:
5109       element = EL_BUG_RIGHT;
5110       break;
5111
5112     case 0x10de:
5113       element = EL_MOLE_UP;
5114       break;
5115
5116     case 0x10e7:
5117       element = EL_MOLE_DOWN;
5118       break;
5119
5120     case 0x10ff:
5121       element = EL_MOLE_LEFT;
5122       break;
5123
5124     case 0x1107:
5125       element = EL_MOLE_RIGHT;
5126       break;
5127
5128     case 0x11c0:
5129       element = EL_ROBOT;
5130       break;
5131
5132     case 0x13f5:
5133       element = EL_YAMYAM_UP;
5134       break;
5135
5136     case 0x1425:
5137       element = EL_SWITCHGATE_OPEN;
5138       break;
5139
5140     case 0x1426:
5141       element = EL_SWITCHGATE_CLOSED;
5142       break;
5143
5144     case 0x1437:
5145       element = EL_DC_SWITCHGATE_SWITCH_UP;
5146       break;
5147
5148     case 0x143a:
5149       element = EL_TIMEGATE_CLOSED;
5150       break;
5151
5152     case 0x144c:        // conveyor belt switch (green)
5153       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5154       break;
5155
5156     case 0x144f:        // conveyor belt switch (red)
5157       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5158       break;
5159
5160     case 0x1452:        // conveyor belt switch (blue)
5161       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5162       break;
5163
5164     case 0x145b:
5165       element = EL_CONVEYOR_BELT_3_MIDDLE;
5166       break;
5167
5168     case 0x1463:
5169       element = EL_CONVEYOR_BELT_3_LEFT;
5170       break;
5171
5172     case 0x146b:
5173       element = EL_CONVEYOR_BELT_3_RIGHT;
5174       break;
5175
5176     case 0x1473:
5177       element = EL_CONVEYOR_BELT_1_MIDDLE;
5178       break;
5179
5180     case 0x147b:
5181       element = EL_CONVEYOR_BELT_1_LEFT;
5182       break;
5183
5184     case 0x1483:
5185       element = EL_CONVEYOR_BELT_1_RIGHT;
5186       break;
5187
5188     case 0x148b:
5189       element = EL_CONVEYOR_BELT_4_MIDDLE;
5190       break;
5191
5192     case 0x1493:
5193       element = EL_CONVEYOR_BELT_4_LEFT;
5194       break;
5195
5196     case 0x149b:
5197       element = EL_CONVEYOR_BELT_4_RIGHT;
5198       break;
5199
5200     case 0x14ac:
5201       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5202       break;
5203
5204     case 0x14bd:
5205       element = EL_EXPANDABLE_WALL_VERTICAL;
5206       break;
5207
5208     case 0x14c6:
5209       element = EL_EXPANDABLE_WALL_ANY;
5210       break;
5211
5212     case 0x14ce:        // growing steel wall (left/right)
5213       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5214       break;
5215
5216     case 0x14df:        // growing steel wall (up/down)
5217       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5218       break;
5219
5220     case 0x14e8:        // growing steel wall (up/down/left/right)
5221       element = EL_EXPANDABLE_STEELWALL_ANY;
5222       break;
5223
5224     case 0x14e9:
5225       element = EL_SHIELD_DEADLY;
5226       break;
5227
5228     case 0x1501:
5229       element = EL_EXTRA_TIME;
5230       break;
5231
5232     case 0x154f:
5233       element = EL_ACID;
5234       break;
5235
5236     case 0x1577:
5237       element = EL_EMPTY_SPACE;
5238       break;
5239
5240     case 0x1578:        // quicksand (empty)
5241       element = EL_QUICKSAND_FAST_EMPTY;
5242       break;
5243
5244     case 0x1579:        // slow quicksand (empty)
5245       element = EL_QUICKSAND_EMPTY;
5246       break;
5247
5248       // 0x157c - 0x158b:
5249       // EL_SAND
5250
5251       // 0x1590 - 0x159f:
5252       // EL_DC_LANDMINE
5253
5254     case 0x15a0:
5255       element = EL_EM_DYNAMITE;
5256       break;
5257
5258     case 0x15a1:        // key (red)
5259       element = EL_EM_KEY_1;
5260       break;
5261
5262     case 0x15a2:        // key (yellow)
5263       element = EL_EM_KEY_2;
5264       break;
5265
5266     case 0x15a3:        // key (blue)
5267       element = EL_EM_KEY_4;
5268       break;
5269
5270     case 0x15a4:        // key (green)
5271       element = EL_EM_KEY_3;
5272       break;
5273
5274     case 0x15a5:        // key (white)
5275       element = EL_DC_KEY_WHITE;
5276       break;
5277
5278     case 0x15a6:
5279       element = EL_WALL_SLIPPERY;
5280       break;
5281
5282     case 0x15a7:
5283       element = EL_WALL;
5284       break;
5285
5286     case 0x15a8:        // wall (not round)
5287       element = EL_WALL;
5288       break;
5289
5290     case 0x15a9:        // (blue)
5291       element = EL_CHAR_A;
5292       break;
5293
5294     case 0x15aa:        // (blue)
5295       element = EL_CHAR_B;
5296       break;
5297
5298     case 0x15ab:        // (blue)
5299       element = EL_CHAR_C;
5300       break;
5301
5302     case 0x15ac:        // (blue)
5303       element = EL_CHAR_D;
5304       break;
5305
5306     case 0x15ad:        // (blue)
5307       element = EL_CHAR_E;
5308       break;
5309
5310     case 0x15ae:        // (blue)
5311       element = EL_CHAR_F;
5312       break;
5313
5314     case 0x15af:        // (blue)
5315       element = EL_CHAR_G;
5316       break;
5317
5318     case 0x15b0:        // (blue)
5319       element = EL_CHAR_H;
5320       break;
5321
5322     case 0x15b1:        // (blue)
5323       element = EL_CHAR_I;
5324       break;
5325
5326     case 0x15b2:        // (blue)
5327       element = EL_CHAR_J;
5328       break;
5329
5330     case 0x15b3:        // (blue)
5331       element = EL_CHAR_K;
5332       break;
5333
5334     case 0x15b4:        // (blue)
5335       element = EL_CHAR_L;
5336       break;
5337
5338     case 0x15b5:        // (blue)
5339       element = EL_CHAR_M;
5340       break;
5341
5342     case 0x15b6:        // (blue)
5343       element = EL_CHAR_N;
5344       break;
5345
5346     case 0x15b7:        // (blue)
5347       element = EL_CHAR_O;
5348       break;
5349
5350     case 0x15b8:        // (blue)
5351       element = EL_CHAR_P;
5352       break;
5353
5354     case 0x15b9:        // (blue)
5355       element = EL_CHAR_Q;
5356       break;
5357
5358     case 0x15ba:        // (blue)
5359       element = EL_CHAR_R;
5360       break;
5361
5362     case 0x15bb:        // (blue)
5363       element = EL_CHAR_S;
5364       break;
5365
5366     case 0x15bc:        // (blue)
5367       element = EL_CHAR_T;
5368       break;
5369
5370     case 0x15bd:        // (blue)
5371       element = EL_CHAR_U;
5372       break;
5373
5374     case 0x15be:        // (blue)
5375       element = EL_CHAR_V;
5376       break;
5377
5378     case 0x15bf:        // (blue)
5379       element = EL_CHAR_W;
5380       break;
5381
5382     case 0x15c0:        // (blue)
5383       element = EL_CHAR_X;
5384       break;
5385
5386     case 0x15c1:        // (blue)
5387       element = EL_CHAR_Y;
5388       break;
5389
5390     case 0x15c2:        // (blue)
5391       element = EL_CHAR_Z;
5392       break;
5393
5394     case 0x15c3:        // (blue)
5395       element = EL_CHAR_AUMLAUT;
5396       break;
5397
5398     case 0x15c4:        // (blue)
5399       element = EL_CHAR_OUMLAUT;
5400       break;
5401
5402     case 0x15c5:        // (blue)
5403       element = EL_CHAR_UUMLAUT;
5404       break;
5405
5406     case 0x15c6:        // (blue)
5407       element = EL_CHAR_0;
5408       break;
5409
5410     case 0x15c7:        // (blue)
5411       element = EL_CHAR_1;
5412       break;
5413
5414     case 0x15c8:        // (blue)
5415       element = EL_CHAR_2;
5416       break;
5417
5418     case 0x15c9:        // (blue)
5419       element = EL_CHAR_3;
5420       break;
5421
5422     case 0x15ca:        // (blue)
5423       element = EL_CHAR_4;
5424       break;
5425
5426     case 0x15cb:        // (blue)
5427       element = EL_CHAR_5;
5428       break;
5429
5430     case 0x15cc:        // (blue)
5431       element = EL_CHAR_6;
5432       break;
5433
5434     case 0x15cd:        // (blue)
5435       element = EL_CHAR_7;
5436       break;
5437
5438     case 0x15ce:        // (blue)
5439       element = EL_CHAR_8;
5440       break;
5441
5442     case 0x15cf:        // (blue)
5443       element = EL_CHAR_9;
5444       break;
5445
5446     case 0x15d0:        // (blue)
5447       element = EL_CHAR_PERIOD;
5448       break;
5449
5450     case 0x15d1:        // (blue)
5451       element = EL_CHAR_EXCLAM;
5452       break;
5453
5454     case 0x15d2:        // (blue)
5455       element = EL_CHAR_COLON;
5456       break;
5457
5458     case 0x15d3:        // (blue)
5459       element = EL_CHAR_LESS;
5460       break;
5461
5462     case 0x15d4:        // (blue)
5463       element = EL_CHAR_GREATER;
5464       break;
5465
5466     case 0x15d5:        // (blue)
5467       element = EL_CHAR_QUESTION;
5468       break;
5469
5470     case 0x15d6:        // (blue)
5471       element = EL_CHAR_COPYRIGHT;
5472       break;
5473
5474     case 0x15d7:        // (blue)
5475       element = EL_CHAR_UP;
5476       break;
5477
5478     case 0x15d8:        // (blue)
5479       element = EL_CHAR_DOWN;
5480       break;
5481
5482     case 0x15d9:        // (blue)
5483       element = EL_CHAR_BUTTON;
5484       break;
5485
5486     case 0x15da:        // (blue)
5487       element = EL_CHAR_PLUS;
5488       break;
5489
5490     case 0x15db:        // (blue)
5491       element = EL_CHAR_MINUS;
5492       break;
5493
5494     case 0x15dc:        // (blue)
5495       element = EL_CHAR_APOSTROPHE;
5496       break;
5497
5498     case 0x15dd:        // (blue)
5499       element = EL_CHAR_PARENLEFT;
5500       break;
5501
5502     case 0x15de:        // (blue)
5503       element = EL_CHAR_PARENRIGHT;
5504       break;
5505
5506     case 0x15df:        // (green)
5507       element = EL_CHAR_A;
5508       break;
5509
5510     case 0x15e0:        // (green)
5511       element = EL_CHAR_B;
5512       break;
5513
5514     case 0x15e1:        // (green)
5515       element = EL_CHAR_C;
5516       break;
5517
5518     case 0x15e2:        // (green)
5519       element = EL_CHAR_D;
5520       break;
5521
5522     case 0x15e3:        // (green)
5523       element = EL_CHAR_E;
5524       break;
5525
5526     case 0x15e4:        // (green)
5527       element = EL_CHAR_F;
5528       break;
5529
5530     case 0x15e5:        // (green)
5531       element = EL_CHAR_G;
5532       break;
5533
5534     case 0x15e6:        // (green)
5535       element = EL_CHAR_H;
5536       break;
5537
5538     case 0x15e7:        // (green)
5539       element = EL_CHAR_I;
5540       break;
5541
5542     case 0x15e8:        // (green)
5543       element = EL_CHAR_J;
5544       break;
5545
5546     case 0x15e9:        // (green)
5547       element = EL_CHAR_K;
5548       break;
5549
5550     case 0x15ea:        // (green)
5551       element = EL_CHAR_L;
5552       break;
5553
5554     case 0x15eb:        // (green)
5555       element = EL_CHAR_M;
5556       break;
5557
5558     case 0x15ec:        // (green)
5559       element = EL_CHAR_N;
5560       break;
5561
5562     case 0x15ed:        // (green)
5563       element = EL_CHAR_O;
5564       break;
5565
5566     case 0x15ee:        // (green)
5567       element = EL_CHAR_P;
5568       break;
5569
5570     case 0x15ef:        // (green)
5571       element = EL_CHAR_Q;
5572       break;
5573
5574     case 0x15f0:        // (green)
5575       element = EL_CHAR_R;
5576       break;
5577
5578     case 0x15f1:        // (green)
5579       element = EL_CHAR_S;
5580       break;
5581
5582     case 0x15f2:        // (green)
5583       element = EL_CHAR_T;
5584       break;
5585
5586     case 0x15f3:        // (green)
5587       element = EL_CHAR_U;
5588       break;
5589
5590     case 0x15f4:        // (green)
5591       element = EL_CHAR_V;
5592       break;
5593
5594     case 0x15f5:        // (green)
5595       element = EL_CHAR_W;
5596       break;
5597
5598     case 0x15f6:        // (green)
5599       element = EL_CHAR_X;
5600       break;
5601
5602     case 0x15f7:        // (green)
5603       element = EL_CHAR_Y;
5604       break;
5605
5606     case 0x15f8:        // (green)
5607       element = EL_CHAR_Z;
5608       break;
5609
5610     case 0x15f9:        // (green)
5611       element = EL_CHAR_AUMLAUT;
5612       break;
5613
5614     case 0x15fa:        // (green)
5615       element = EL_CHAR_OUMLAUT;
5616       break;
5617
5618     case 0x15fb:        // (green)
5619       element = EL_CHAR_UUMLAUT;
5620       break;
5621
5622     case 0x15fc:        // (green)
5623       element = EL_CHAR_0;
5624       break;
5625
5626     case 0x15fd:        // (green)
5627       element = EL_CHAR_1;
5628       break;
5629
5630     case 0x15fe:        // (green)
5631       element = EL_CHAR_2;
5632       break;
5633
5634     case 0x15ff:        // (green)
5635       element = EL_CHAR_3;
5636       break;
5637
5638     case 0x1600:        // (green)
5639       element = EL_CHAR_4;
5640       break;
5641
5642     case 0x1601:        // (green)
5643       element = EL_CHAR_5;
5644       break;
5645
5646     case 0x1602:        // (green)
5647       element = EL_CHAR_6;
5648       break;
5649
5650     case 0x1603:        // (green)
5651       element = EL_CHAR_7;
5652       break;
5653
5654     case 0x1604:        // (green)
5655       element = EL_CHAR_8;
5656       break;
5657
5658     case 0x1605:        // (green)
5659       element = EL_CHAR_9;
5660       break;
5661
5662     case 0x1606:        // (green)
5663       element = EL_CHAR_PERIOD;
5664       break;
5665
5666     case 0x1607:        // (green)
5667       element = EL_CHAR_EXCLAM;
5668       break;
5669
5670     case 0x1608:        // (green)
5671       element = EL_CHAR_COLON;
5672       break;
5673
5674     case 0x1609:        // (green)
5675       element = EL_CHAR_LESS;
5676       break;
5677
5678     case 0x160a:        // (green)
5679       element = EL_CHAR_GREATER;
5680       break;
5681
5682     case 0x160b:        // (green)
5683       element = EL_CHAR_QUESTION;
5684       break;
5685
5686     case 0x160c:        // (green)
5687       element = EL_CHAR_COPYRIGHT;
5688       break;
5689
5690     case 0x160d:        // (green)
5691       element = EL_CHAR_UP;
5692       break;
5693
5694     case 0x160e:        // (green)
5695       element = EL_CHAR_DOWN;
5696       break;
5697
5698     case 0x160f:        // (green)
5699       element = EL_CHAR_BUTTON;
5700       break;
5701
5702     case 0x1610:        // (green)
5703       element = EL_CHAR_PLUS;
5704       break;
5705
5706     case 0x1611:        // (green)
5707       element = EL_CHAR_MINUS;
5708       break;
5709
5710     case 0x1612:        // (green)
5711       element = EL_CHAR_APOSTROPHE;
5712       break;
5713
5714     case 0x1613:        // (green)
5715       element = EL_CHAR_PARENLEFT;
5716       break;
5717
5718     case 0x1614:        // (green)
5719       element = EL_CHAR_PARENRIGHT;
5720       break;
5721
5722     case 0x1615:        // (blue steel)
5723       element = EL_STEEL_CHAR_A;
5724       break;
5725
5726     case 0x1616:        // (blue steel)
5727       element = EL_STEEL_CHAR_B;
5728       break;
5729
5730     case 0x1617:        // (blue steel)
5731       element = EL_STEEL_CHAR_C;
5732       break;
5733
5734     case 0x1618:        // (blue steel)
5735       element = EL_STEEL_CHAR_D;
5736       break;
5737
5738     case 0x1619:        // (blue steel)
5739       element = EL_STEEL_CHAR_E;
5740       break;
5741
5742     case 0x161a:        // (blue steel)
5743       element = EL_STEEL_CHAR_F;
5744       break;
5745
5746     case 0x161b:        // (blue steel)
5747       element = EL_STEEL_CHAR_G;
5748       break;
5749
5750     case 0x161c:        // (blue steel)
5751       element = EL_STEEL_CHAR_H;
5752       break;
5753
5754     case 0x161d:        // (blue steel)
5755       element = EL_STEEL_CHAR_I;
5756       break;
5757
5758     case 0x161e:        // (blue steel)
5759       element = EL_STEEL_CHAR_J;
5760       break;
5761
5762     case 0x161f:        // (blue steel)
5763       element = EL_STEEL_CHAR_K;
5764       break;
5765
5766     case 0x1620:        // (blue steel)
5767       element = EL_STEEL_CHAR_L;
5768       break;
5769
5770     case 0x1621:        // (blue steel)
5771       element = EL_STEEL_CHAR_M;
5772       break;
5773
5774     case 0x1622:        // (blue steel)
5775       element = EL_STEEL_CHAR_N;
5776       break;
5777
5778     case 0x1623:        // (blue steel)
5779       element = EL_STEEL_CHAR_O;
5780       break;
5781
5782     case 0x1624:        // (blue steel)
5783       element = EL_STEEL_CHAR_P;
5784       break;
5785
5786     case 0x1625:        // (blue steel)
5787       element = EL_STEEL_CHAR_Q;
5788       break;
5789
5790     case 0x1626:        // (blue steel)
5791       element = EL_STEEL_CHAR_R;
5792       break;
5793
5794     case 0x1627:        // (blue steel)
5795       element = EL_STEEL_CHAR_S;
5796       break;
5797
5798     case 0x1628:        // (blue steel)
5799       element = EL_STEEL_CHAR_T;
5800       break;
5801
5802     case 0x1629:        // (blue steel)
5803       element = EL_STEEL_CHAR_U;
5804       break;
5805
5806     case 0x162a:        // (blue steel)
5807       element = EL_STEEL_CHAR_V;
5808       break;
5809
5810     case 0x162b:        // (blue steel)
5811       element = EL_STEEL_CHAR_W;
5812       break;
5813
5814     case 0x162c:        // (blue steel)
5815       element = EL_STEEL_CHAR_X;
5816       break;
5817
5818     case 0x162d:        // (blue steel)
5819       element = EL_STEEL_CHAR_Y;
5820       break;
5821
5822     case 0x162e:        // (blue steel)
5823       element = EL_STEEL_CHAR_Z;
5824       break;
5825
5826     case 0x162f:        // (blue steel)
5827       element = EL_STEEL_CHAR_AUMLAUT;
5828       break;
5829
5830     case 0x1630:        // (blue steel)
5831       element = EL_STEEL_CHAR_OUMLAUT;
5832       break;
5833
5834     case 0x1631:        // (blue steel)
5835       element = EL_STEEL_CHAR_UUMLAUT;
5836       break;
5837
5838     case 0x1632:        // (blue steel)
5839       element = EL_STEEL_CHAR_0;
5840       break;
5841
5842     case 0x1633:        // (blue steel)
5843       element = EL_STEEL_CHAR_1;
5844       break;
5845
5846     case 0x1634:        // (blue steel)
5847       element = EL_STEEL_CHAR_2;
5848       break;
5849
5850     case 0x1635:        // (blue steel)
5851       element = EL_STEEL_CHAR_3;
5852       break;
5853
5854     case 0x1636:        // (blue steel)
5855       element = EL_STEEL_CHAR_4;
5856       break;
5857
5858     case 0x1637:        // (blue steel)
5859       element = EL_STEEL_CHAR_5;
5860       break;
5861
5862     case 0x1638:        // (blue steel)
5863       element = EL_STEEL_CHAR_6;
5864       break;
5865
5866     case 0x1639:        // (blue steel)
5867       element = EL_STEEL_CHAR_7;
5868       break;
5869
5870     case 0x163a:        // (blue steel)
5871       element = EL_STEEL_CHAR_8;
5872       break;
5873
5874     case 0x163b:        // (blue steel)
5875       element = EL_STEEL_CHAR_9;
5876       break;
5877
5878     case 0x163c:        // (blue steel)
5879       element = EL_STEEL_CHAR_PERIOD;
5880       break;
5881
5882     case 0x163d:        // (blue steel)
5883       element = EL_STEEL_CHAR_EXCLAM;
5884       break;
5885
5886     case 0x163e:        // (blue steel)
5887       element = EL_STEEL_CHAR_COLON;
5888       break;
5889
5890     case 0x163f:        // (blue steel)
5891       element = EL_STEEL_CHAR_LESS;
5892       break;
5893
5894     case 0x1640:        // (blue steel)
5895       element = EL_STEEL_CHAR_GREATER;
5896       break;
5897
5898     case 0x1641:        // (blue steel)
5899       element = EL_STEEL_CHAR_QUESTION;
5900       break;
5901
5902     case 0x1642:        // (blue steel)
5903       element = EL_STEEL_CHAR_COPYRIGHT;
5904       break;
5905
5906     case 0x1643:        // (blue steel)
5907       element = EL_STEEL_CHAR_UP;
5908       break;
5909
5910     case 0x1644:        // (blue steel)
5911       element = EL_STEEL_CHAR_DOWN;
5912       break;
5913
5914     case 0x1645:        // (blue steel)
5915       element = EL_STEEL_CHAR_BUTTON;
5916       break;
5917
5918     case 0x1646:        // (blue steel)
5919       element = EL_STEEL_CHAR_PLUS;
5920       break;
5921
5922     case 0x1647:        // (blue steel)
5923       element = EL_STEEL_CHAR_MINUS;
5924       break;
5925
5926     case 0x1648:        // (blue steel)
5927       element = EL_STEEL_CHAR_APOSTROPHE;
5928       break;
5929
5930     case 0x1649:        // (blue steel)
5931       element = EL_STEEL_CHAR_PARENLEFT;
5932       break;
5933
5934     case 0x164a:        // (blue steel)
5935       element = EL_STEEL_CHAR_PARENRIGHT;
5936       break;
5937
5938     case 0x164b:        // (green steel)
5939       element = EL_STEEL_CHAR_A;
5940       break;
5941
5942     case 0x164c:        // (green steel)
5943       element = EL_STEEL_CHAR_B;
5944       break;
5945
5946     case 0x164d:        // (green steel)
5947       element = EL_STEEL_CHAR_C;
5948       break;
5949
5950     case 0x164e:        // (green steel)
5951       element = EL_STEEL_CHAR_D;
5952       break;
5953
5954     case 0x164f:        // (green steel)
5955       element = EL_STEEL_CHAR_E;
5956       break;
5957
5958     case 0x1650:        // (green steel)
5959       element = EL_STEEL_CHAR_F;
5960       break;
5961
5962     case 0x1651:        // (green steel)
5963       element = EL_STEEL_CHAR_G;
5964       break;
5965
5966     case 0x1652:        // (green steel)
5967       element = EL_STEEL_CHAR_H;
5968       break;
5969
5970     case 0x1653:        // (green steel)
5971       element = EL_STEEL_CHAR_I;
5972       break;
5973
5974     case 0x1654:        // (green steel)
5975       element = EL_STEEL_CHAR_J;
5976       break;
5977
5978     case 0x1655:        // (green steel)
5979       element = EL_STEEL_CHAR_K;
5980       break;
5981
5982     case 0x1656:        // (green steel)
5983       element = EL_STEEL_CHAR_L;
5984       break;
5985
5986     case 0x1657:        // (green steel)
5987       element = EL_STEEL_CHAR_M;
5988       break;
5989
5990     case 0x1658:        // (green steel)
5991       element = EL_STEEL_CHAR_N;
5992       break;
5993
5994     case 0x1659:        // (green steel)
5995       element = EL_STEEL_CHAR_O;
5996       break;
5997
5998     case 0x165a:        // (green steel)
5999       element = EL_STEEL_CHAR_P;
6000       break;
6001
6002     case 0x165b:        // (green steel)
6003       element = EL_STEEL_CHAR_Q;
6004       break;
6005
6006     case 0x165c:        // (green steel)
6007       element = EL_STEEL_CHAR_R;
6008       break;
6009
6010     case 0x165d:        // (green steel)
6011       element = EL_STEEL_CHAR_S;
6012       break;
6013
6014     case 0x165e:        // (green steel)
6015       element = EL_STEEL_CHAR_T;
6016       break;
6017
6018     case 0x165f:        // (green steel)
6019       element = EL_STEEL_CHAR_U;
6020       break;
6021
6022     case 0x1660:        // (green steel)
6023       element = EL_STEEL_CHAR_V;
6024       break;
6025
6026     case 0x1661:        // (green steel)
6027       element = EL_STEEL_CHAR_W;
6028       break;
6029
6030     case 0x1662:        // (green steel)
6031       element = EL_STEEL_CHAR_X;
6032       break;
6033
6034     case 0x1663:        // (green steel)
6035       element = EL_STEEL_CHAR_Y;
6036       break;
6037
6038     case 0x1664:        // (green steel)
6039       element = EL_STEEL_CHAR_Z;
6040       break;
6041
6042     case 0x1665:        // (green steel)
6043       element = EL_STEEL_CHAR_AUMLAUT;
6044       break;
6045
6046     case 0x1666:        // (green steel)
6047       element = EL_STEEL_CHAR_OUMLAUT;
6048       break;
6049
6050     case 0x1667:        // (green steel)
6051       element = EL_STEEL_CHAR_UUMLAUT;
6052       break;
6053
6054     case 0x1668:        // (green steel)
6055       element = EL_STEEL_CHAR_0;
6056       break;
6057
6058     case 0x1669:        // (green steel)
6059       element = EL_STEEL_CHAR_1;
6060       break;
6061
6062     case 0x166a:        // (green steel)
6063       element = EL_STEEL_CHAR_2;
6064       break;
6065
6066     case 0x166b:        // (green steel)
6067       element = EL_STEEL_CHAR_3;
6068       break;
6069
6070     case 0x166c:        // (green steel)
6071       element = EL_STEEL_CHAR_4;
6072       break;
6073
6074     case 0x166d:        // (green steel)
6075       element = EL_STEEL_CHAR_5;
6076       break;
6077
6078     case 0x166e:        // (green steel)
6079       element = EL_STEEL_CHAR_6;
6080       break;
6081
6082     case 0x166f:        // (green steel)
6083       element = EL_STEEL_CHAR_7;
6084       break;
6085
6086     case 0x1670:        // (green steel)
6087       element = EL_STEEL_CHAR_8;
6088       break;
6089
6090     case 0x1671:        // (green steel)
6091       element = EL_STEEL_CHAR_9;
6092       break;
6093
6094     case 0x1672:        // (green steel)
6095       element = EL_STEEL_CHAR_PERIOD;
6096       break;
6097
6098     case 0x1673:        // (green steel)
6099       element = EL_STEEL_CHAR_EXCLAM;
6100       break;
6101
6102     case 0x1674:        // (green steel)
6103       element = EL_STEEL_CHAR_COLON;
6104       break;
6105
6106     case 0x1675:        // (green steel)
6107       element = EL_STEEL_CHAR_LESS;
6108       break;
6109
6110     case 0x1676:        // (green steel)
6111       element = EL_STEEL_CHAR_GREATER;
6112       break;
6113
6114     case 0x1677:        // (green steel)
6115       element = EL_STEEL_CHAR_QUESTION;
6116       break;
6117
6118     case 0x1678:        // (green steel)
6119       element = EL_STEEL_CHAR_COPYRIGHT;
6120       break;
6121
6122     case 0x1679:        // (green steel)
6123       element = EL_STEEL_CHAR_UP;
6124       break;
6125
6126     case 0x167a:        // (green steel)
6127       element = EL_STEEL_CHAR_DOWN;
6128       break;
6129
6130     case 0x167b:        // (green steel)
6131       element = EL_STEEL_CHAR_BUTTON;
6132       break;
6133
6134     case 0x167c:        // (green steel)
6135       element = EL_STEEL_CHAR_PLUS;
6136       break;
6137
6138     case 0x167d:        // (green steel)
6139       element = EL_STEEL_CHAR_MINUS;
6140       break;
6141
6142     case 0x167e:        // (green steel)
6143       element = EL_STEEL_CHAR_APOSTROPHE;
6144       break;
6145
6146     case 0x167f:        // (green steel)
6147       element = EL_STEEL_CHAR_PARENLEFT;
6148       break;
6149
6150     case 0x1680:        // (green steel)
6151       element = EL_STEEL_CHAR_PARENRIGHT;
6152       break;
6153
6154     case 0x1681:        // gate (red)
6155       element = EL_EM_GATE_1;
6156       break;
6157
6158     case 0x1682:        // secret gate (red)
6159       element = EL_EM_GATE_1_GRAY;
6160       break;
6161
6162     case 0x1683:        // gate (yellow)
6163       element = EL_EM_GATE_2;
6164       break;
6165
6166     case 0x1684:        // secret gate (yellow)
6167       element = EL_EM_GATE_2_GRAY;
6168       break;
6169
6170     case 0x1685:        // gate (blue)
6171       element = EL_EM_GATE_4;
6172       break;
6173
6174     case 0x1686:        // secret gate (blue)
6175       element = EL_EM_GATE_4_GRAY;
6176       break;
6177
6178     case 0x1687:        // gate (green)
6179       element = EL_EM_GATE_3;
6180       break;
6181
6182     case 0x1688:        // secret gate (green)
6183       element = EL_EM_GATE_3_GRAY;
6184       break;
6185
6186     case 0x1689:        // gate (white)
6187       element = EL_DC_GATE_WHITE;
6188       break;
6189
6190     case 0x168a:        // secret gate (white)
6191       element = EL_DC_GATE_WHITE_GRAY;
6192       break;
6193
6194     case 0x168b:        // secret gate (no key)
6195       element = EL_DC_GATE_FAKE_GRAY;
6196       break;
6197
6198     case 0x168c:
6199       element = EL_ROBOT_WHEEL;
6200       break;
6201
6202     case 0x168d:
6203       element = EL_DC_TIMEGATE_SWITCH;
6204       break;
6205
6206     case 0x168e:
6207       element = EL_ACID_POOL_BOTTOM;
6208       break;
6209
6210     case 0x168f:
6211       element = EL_ACID_POOL_TOPLEFT;
6212       break;
6213
6214     case 0x1690:
6215       element = EL_ACID_POOL_TOPRIGHT;
6216       break;
6217
6218     case 0x1691:
6219       element = EL_ACID_POOL_BOTTOMLEFT;
6220       break;
6221
6222     case 0x1692:
6223       element = EL_ACID_POOL_BOTTOMRIGHT;
6224       break;
6225
6226     case 0x1693:
6227       element = EL_STEELWALL;
6228       break;
6229
6230     case 0x1694:
6231       element = EL_STEELWALL_SLIPPERY;
6232       break;
6233
6234     case 0x1695:        // steel wall (not round)
6235       element = EL_STEELWALL;
6236       break;
6237
6238     case 0x1696:        // steel wall (left)
6239       element = EL_DC_STEELWALL_1_LEFT;
6240       break;
6241
6242     case 0x1697:        // steel wall (bottom)
6243       element = EL_DC_STEELWALL_1_BOTTOM;
6244       break;
6245
6246     case 0x1698:        // steel wall (right)
6247       element = EL_DC_STEELWALL_1_RIGHT;
6248       break;
6249
6250     case 0x1699:        // steel wall (top)
6251       element = EL_DC_STEELWALL_1_TOP;
6252       break;
6253
6254     case 0x169a:        // steel wall (left/bottom)
6255       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6256       break;
6257
6258     case 0x169b:        // steel wall (right/bottom)
6259       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6260       break;
6261
6262     case 0x169c:        // steel wall (right/top)
6263       element = EL_DC_STEELWALL_1_TOPRIGHT;
6264       break;
6265
6266     case 0x169d:        // steel wall (left/top)
6267       element = EL_DC_STEELWALL_1_TOPLEFT;
6268       break;
6269
6270     case 0x169e:        // steel wall (right/bottom small)
6271       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6272       break;
6273
6274     case 0x169f:        // steel wall (left/bottom small)
6275       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6276       break;
6277
6278     case 0x16a0:        // steel wall (right/top small)
6279       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6280       break;
6281
6282     case 0x16a1:        // steel wall (left/top small)
6283       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6284       break;
6285
6286     case 0x16a2:        // steel wall (left/right)
6287       element = EL_DC_STEELWALL_1_VERTICAL;
6288       break;
6289
6290     case 0x16a3:        // steel wall (top/bottom)
6291       element = EL_DC_STEELWALL_1_HORIZONTAL;
6292       break;
6293
6294     case 0x16a4:        // steel wall 2 (left end)
6295       element = EL_DC_STEELWALL_2_LEFT;
6296       break;
6297
6298     case 0x16a5:        // steel wall 2 (right end)
6299       element = EL_DC_STEELWALL_2_RIGHT;
6300       break;
6301
6302     case 0x16a6:        // steel wall 2 (top end)
6303       element = EL_DC_STEELWALL_2_TOP;
6304       break;
6305
6306     case 0x16a7:        // steel wall 2 (bottom end)
6307       element = EL_DC_STEELWALL_2_BOTTOM;
6308       break;
6309
6310     case 0x16a8:        // steel wall 2 (left/right)
6311       element = EL_DC_STEELWALL_2_HORIZONTAL;
6312       break;
6313
6314     case 0x16a9:        // steel wall 2 (up/down)
6315       element = EL_DC_STEELWALL_2_VERTICAL;
6316       break;
6317
6318     case 0x16aa:        // steel wall 2 (mid)
6319       element = EL_DC_STEELWALL_2_MIDDLE;
6320       break;
6321
6322     case 0x16ab:
6323       element = EL_SIGN_EXCLAMATION;
6324       break;
6325
6326     case 0x16ac:
6327       element = EL_SIGN_RADIOACTIVITY;
6328       break;
6329
6330     case 0x16ad:
6331       element = EL_SIGN_STOP;
6332       break;
6333
6334     case 0x16ae:
6335       element = EL_SIGN_WHEELCHAIR;
6336       break;
6337
6338     case 0x16af:
6339       element = EL_SIGN_PARKING;
6340       break;
6341
6342     case 0x16b0:
6343       element = EL_SIGN_NO_ENTRY;
6344       break;
6345
6346     case 0x16b1:
6347       element = EL_SIGN_HEART;
6348       break;
6349
6350     case 0x16b2:
6351       element = EL_SIGN_GIVE_WAY;
6352       break;
6353
6354     case 0x16b3:
6355       element = EL_SIGN_ENTRY_FORBIDDEN;
6356       break;
6357
6358     case 0x16b4:
6359       element = EL_SIGN_EMERGENCY_EXIT;
6360       break;
6361
6362     case 0x16b5:
6363       element = EL_SIGN_YIN_YANG;
6364       break;
6365
6366     case 0x16b6:
6367       element = EL_WALL_EMERALD;
6368       break;
6369
6370     case 0x16b7:
6371       element = EL_WALL_DIAMOND;
6372       break;
6373
6374     case 0x16b8:
6375       element = EL_WALL_PEARL;
6376       break;
6377
6378     case 0x16b9:
6379       element = EL_WALL_CRYSTAL;
6380       break;
6381
6382     case 0x16ba:
6383       element = EL_INVISIBLE_WALL;
6384       break;
6385
6386     case 0x16bb:
6387       element = EL_INVISIBLE_STEELWALL;
6388       break;
6389
6390       // 0x16bc - 0x16cb:
6391       // EL_INVISIBLE_SAND
6392
6393     case 0x16cc:
6394       element = EL_LIGHT_SWITCH;
6395       break;
6396
6397     case 0x16cd:
6398       element = EL_ENVELOPE_1;
6399       break;
6400
6401     default:
6402       if (element >= 0x0117 && element <= 0x036e)       // (?)
6403         element = EL_DIAMOND;
6404       else if (element >= 0x042d && element <= 0x0684)  // (?)
6405         element = EL_EMERALD;
6406       else if (element >= 0x157c && element <= 0x158b)
6407         element = EL_SAND;
6408       else if (element >= 0x1590 && element <= 0x159f)
6409         element = EL_DC_LANDMINE;
6410       else if (element >= 0x16bc && element <= 0x16cb)
6411         element = EL_INVISIBLE_SAND;
6412       else
6413       {
6414         Warn("unknown Diamond Caves element 0x%04x", element);
6415
6416         element = EL_UNKNOWN;
6417       }
6418       break;
6419   }
6420
6421   return getMappedElement(element);
6422 }
6423
6424 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6425 {
6426   byte header[DC_LEVEL_HEADER_SIZE];
6427   int envelope_size;
6428   int envelope_header_pos = 62;
6429   int envelope_content_pos = 94;
6430   int level_name_pos = 251;
6431   int level_author_pos = 292;
6432   int envelope_header_len;
6433   int envelope_content_len;
6434   int level_name_len;
6435   int level_author_len;
6436   int fieldx, fieldy;
6437   int num_yamyam_contents;
6438   int i, x, y;
6439
6440   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6441
6442   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6443   {
6444     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6445
6446     header[i * 2 + 0] = header_word >> 8;
6447     header[i * 2 + 1] = header_word & 0xff;
6448   }
6449
6450   // read some values from level header to check level decoding integrity
6451   fieldx = header[6] | (header[7] << 8);
6452   fieldy = header[8] | (header[9] << 8);
6453   num_yamyam_contents = header[60] | (header[61] << 8);
6454
6455   // do some simple sanity checks to ensure that level was correctly decoded
6456   if (fieldx < 1 || fieldx > 256 ||
6457       fieldy < 1 || fieldy > 256 ||
6458       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6459   {
6460     level->no_valid_file = TRUE;
6461
6462     Warn("cannot decode level from stream -- using empty level");
6463
6464     return;
6465   }
6466
6467   // maximum envelope header size is 31 bytes
6468   envelope_header_len   = header[envelope_header_pos];
6469   // maximum envelope content size is 110 (156?) bytes
6470   envelope_content_len  = header[envelope_content_pos];
6471
6472   // maximum level title size is 40 bytes
6473   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6474   // maximum level author size is 30 (51?) bytes
6475   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6476
6477   envelope_size = 0;
6478
6479   for (i = 0; i < envelope_header_len; i++)
6480     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6481       level->envelope[0].text[envelope_size++] =
6482         header[envelope_header_pos + 1 + i];
6483
6484   if (envelope_header_len > 0 && envelope_content_len > 0)
6485   {
6486     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6487       level->envelope[0].text[envelope_size++] = '\n';
6488     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6489       level->envelope[0].text[envelope_size++] = '\n';
6490   }
6491
6492   for (i = 0; i < envelope_content_len; i++)
6493     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6494       level->envelope[0].text[envelope_size++] =
6495         header[envelope_content_pos + 1 + i];
6496
6497   level->envelope[0].text[envelope_size] = '\0';
6498
6499   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6500   level->envelope[0].ysize = 10;
6501   level->envelope[0].autowrap = TRUE;
6502   level->envelope[0].centered = TRUE;
6503
6504   for (i = 0; i < level_name_len; i++)
6505     level->name[i] = header[level_name_pos + 1 + i];
6506   level->name[level_name_len] = '\0';
6507
6508   for (i = 0; i < level_author_len; i++)
6509     level->author[i] = header[level_author_pos + 1 + i];
6510   level->author[level_author_len] = '\0';
6511
6512   num_yamyam_contents = header[60] | (header[61] << 8);
6513   level->num_yamyam_contents =
6514     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6515
6516   for (i = 0; i < num_yamyam_contents; i++)
6517   {
6518     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6519     {
6520       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6521       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6522
6523       if (i < MAX_ELEMENT_CONTENTS)
6524         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6525     }
6526   }
6527
6528   fieldx = header[6] | (header[7] << 8);
6529   fieldy = header[8] | (header[9] << 8);
6530   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6531   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6532
6533   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6534   {
6535     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6536     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6537
6538     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6539       level->field[x][y] = getMappedElement_DC(element_dc);
6540   }
6541
6542   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6543   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6544   level->field[x][y] = EL_PLAYER_1;
6545
6546   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6547   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6548   level->field[x][y] = EL_PLAYER_2;
6549
6550   level->gems_needed            = header[18] | (header[19] << 8);
6551
6552   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6553   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6554   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6555   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6556   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6557   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6558   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6559   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6560   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6561   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6562   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6563   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6564
6565   level->time                   = header[44] | (header[45] << 8);
6566
6567   level->amoeba_speed           = header[46] | (header[47] << 8);
6568   level->time_light             = header[48] | (header[49] << 8);
6569   level->time_timegate          = header[50] | (header[51] << 8);
6570   level->time_wheel             = header[52] | (header[53] << 8);
6571   level->time_magic_wall        = header[54] | (header[55] << 8);
6572   level->extra_time             = header[56] | (header[57] << 8);
6573   level->shield_normal_time     = header[58] | (header[59] << 8);
6574
6575   // shield and extra time elements do not have a score
6576   level->score[SC_SHIELD]       = 0;
6577   level->extra_time_score       = 0;
6578
6579   // set time for normal and deadly shields to the same value
6580   level->shield_deadly_time     = level->shield_normal_time;
6581
6582   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6583   // can slip down from flat walls, like normal walls and steel walls
6584   level->em_slippery_gems = TRUE;
6585
6586   // time score is counted for each 10 seconds left in Diamond Caves levels
6587   level->time_score_base = 10;
6588 }
6589
6590 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6591                                      struct LevelFileInfo *level_file_info,
6592                                      boolean level_info_only)
6593 {
6594   char *filename = level_file_info->filename;
6595   File *file;
6596   int num_magic_bytes = 8;
6597   char magic_bytes[num_magic_bytes + 1];
6598   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6599
6600   if (!(file = openFile(filename, MODE_READ)))
6601   {
6602     level->no_valid_file = TRUE;
6603
6604     if (!level_info_only)
6605       Warn("cannot read level '%s' -- using empty level", filename);
6606
6607     return;
6608   }
6609
6610   // fseek(file, 0x0000, SEEK_SET);
6611
6612   if (level_file_info->packed)
6613   {
6614     // read "magic bytes" from start of file
6615     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6616       magic_bytes[0] = '\0';
6617
6618     // check "magic bytes" for correct file format
6619     if (!strPrefix(magic_bytes, "DC2"))
6620     {
6621       level->no_valid_file = TRUE;
6622
6623       Warn("unknown DC level file '%s' -- using empty level", filename);
6624
6625       return;
6626     }
6627
6628     if (strPrefix(magic_bytes, "DC2Win95") ||
6629         strPrefix(magic_bytes, "DC2Win98"))
6630     {
6631       int position_first_level = 0x00fa;
6632       int extra_bytes = 4;
6633       int skip_bytes;
6634
6635       // advance file stream to first level inside the level package
6636       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6637
6638       // each block of level data is followed by block of non-level data
6639       num_levels_to_skip *= 2;
6640
6641       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6642       while (num_levels_to_skip >= 0)
6643       {
6644         // advance file stream to next level inside the level package
6645         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6646         {
6647           level->no_valid_file = TRUE;
6648
6649           Warn("cannot fseek in file '%s' -- using empty level", filename);
6650
6651           return;
6652         }
6653
6654         // skip apparently unused extra bytes following each level
6655         ReadUnusedBytesFromFile(file, extra_bytes);
6656
6657         // read size of next level in level package
6658         skip_bytes = getFile32BitLE(file);
6659
6660         num_levels_to_skip--;
6661       }
6662     }
6663     else
6664     {
6665       level->no_valid_file = TRUE;
6666
6667       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6668
6669       return;
6670     }
6671   }
6672
6673   LoadLevelFromFileStream_DC(file, level);
6674
6675   closeFile(file);
6676 }
6677
6678
6679 // ----------------------------------------------------------------------------
6680 // functions for loading SB level
6681 // ----------------------------------------------------------------------------
6682
6683 int getMappedElement_SB(int element_ascii, boolean use_ces)
6684 {
6685   static struct
6686   {
6687     int ascii;
6688     int sb;
6689     int ce;
6690   }
6691   sb_element_mapping[] =
6692   {
6693     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6694     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6695     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6696     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6697     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6698     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6699     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6700     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6701
6702     { 0,   -1,                      -1          },
6703   };
6704
6705   int i;
6706
6707   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6708     if (element_ascii == sb_element_mapping[i].ascii)
6709       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6710
6711   return EL_UNDEFINED;
6712 }
6713
6714 static void SetLevelSettings_SB(struct LevelInfo *level)
6715 {
6716   // time settings
6717   level->time = 0;
6718   level->use_step_counter = TRUE;
6719
6720   // score settings
6721   level->score[SC_TIME_BONUS] = 0;
6722   level->time_score_base = 1;
6723   level->rate_time_over_score = TRUE;
6724
6725   // game settings
6726   level->auto_exit_sokoban = TRUE;
6727 }
6728
6729 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6730                                      struct LevelFileInfo *level_file_info,
6731                                      boolean level_info_only)
6732 {
6733   char *filename = level_file_info->filename;
6734   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6735   char last_comment[MAX_LINE_LEN];
6736   char level_name[MAX_LINE_LEN];
6737   char *line_ptr;
6738   File *file;
6739   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6740   boolean read_continued_line = FALSE;
6741   boolean reading_playfield = FALSE;
6742   boolean got_valid_playfield_line = FALSE;
6743   boolean invalid_playfield_char = FALSE;
6744   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6745   int file_level_nr = 0;
6746   int x = 0, y = 0;             // initialized to make compilers happy
6747
6748   last_comment[0] = '\0';
6749   level_name[0] = '\0';
6750
6751   if (!(file = openFile(filename, MODE_READ)))
6752   {
6753     level->no_valid_file = TRUE;
6754
6755     if (!level_info_only)
6756       Warn("cannot read level '%s' -- using empty level", filename);
6757
6758     return;
6759   }
6760
6761   while (!checkEndOfFile(file))
6762   {
6763     // level successfully read, but next level may follow here
6764     if (!got_valid_playfield_line && reading_playfield)
6765     {
6766       // read playfield from single level file -- skip remaining file
6767       if (!level_file_info->packed)
6768         break;
6769
6770       if (file_level_nr >= num_levels_to_skip)
6771         break;
6772
6773       file_level_nr++;
6774
6775       last_comment[0] = '\0';
6776       level_name[0] = '\0';
6777
6778       reading_playfield = FALSE;
6779     }
6780
6781     got_valid_playfield_line = FALSE;
6782
6783     // read next line of input file
6784     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6785       break;
6786
6787     // cut trailing line break (this can be newline and/or carriage return)
6788     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6789       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6790         *line_ptr = '\0';
6791
6792     // copy raw input line for later use (mainly debugging output)
6793     strcpy(line_raw, line);
6794
6795     if (read_continued_line)
6796     {
6797       // append new line to existing line, if there is enough space
6798       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6799         strcat(previous_line, line_ptr);
6800
6801       strcpy(line, previous_line);      // copy storage buffer to line
6802
6803       read_continued_line = FALSE;
6804     }
6805
6806     // if the last character is '\', continue at next line
6807     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6808     {
6809       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6810       strcpy(previous_line, line);      // copy line to storage buffer
6811
6812       read_continued_line = TRUE;
6813
6814       continue;
6815     }
6816
6817     // skip empty lines
6818     if (line[0] == '\0')
6819       continue;
6820
6821     // extract comment text from comment line
6822     if (line[0] == ';')
6823     {
6824       for (line_ptr = line; *line_ptr; line_ptr++)
6825         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6826           break;
6827
6828       strcpy(last_comment, line_ptr);
6829
6830       continue;
6831     }
6832
6833     // extract level title text from line containing level title
6834     if (line[0] == '\'')
6835     {
6836       strcpy(level_name, &line[1]);
6837
6838       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6839         level_name[strlen(level_name) - 1] = '\0';
6840
6841       continue;
6842     }
6843
6844     // skip lines containing only spaces (or empty lines)
6845     for (line_ptr = line; *line_ptr; line_ptr++)
6846       if (*line_ptr != ' ')
6847         break;
6848     if (*line_ptr == '\0')
6849       continue;
6850
6851     // at this point, we have found a line containing part of a playfield
6852
6853     got_valid_playfield_line = TRUE;
6854
6855     if (!reading_playfield)
6856     {
6857       reading_playfield = TRUE;
6858       invalid_playfield_char = FALSE;
6859
6860       for (x = 0; x < MAX_LEV_FIELDX; x++)
6861         for (y = 0; y < MAX_LEV_FIELDY; y++)
6862           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6863
6864       level->fieldx = 0;
6865       level->fieldy = 0;
6866
6867       // start with topmost tile row
6868       y = 0;
6869     }
6870
6871     // skip playfield line if larger row than allowed
6872     if (y >= MAX_LEV_FIELDY)
6873       continue;
6874
6875     // start with leftmost tile column
6876     x = 0;
6877
6878     // read playfield elements from line
6879     for (line_ptr = line; *line_ptr; line_ptr++)
6880     {
6881       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6882
6883       // stop parsing playfield line if larger column than allowed
6884       if (x >= MAX_LEV_FIELDX)
6885         break;
6886
6887       if (mapped_sb_element == EL_UNDEFINED)
6888       {
6889         invalid_playfield_char = TRUE;
6890
6891         break;
6892       }
6893
6894       level->field[x][y] = mapped_sb_element;
6895
6896       // continue with next tile column
6897       x++;
6898
6899       level->fieldx = MAX(x, level->fieldx);
6900     }
6901
6902     if (invalid_playfield_char)
6903     {
6904       // if first playfield line, treat invalid lines as comment lines
6905       if (y == 0)
6906         reading_playfield = FALSE;
6907
6908       continue;
6909     }
6910
6911     // continue with next tile row
6912     y++;
6913   }
6914
6915   closeFile(file);
6916
6917   level->fieldy = y;
6918
6919   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6920   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6921
6922   if (!reading_playfield)
6923   {
6924     level->no_valid_file = TRUE;
6925
6926     Warn("cannot read level '%s' -- using empty level", filename);
6927
6928     return;
6929   }
6930
6931   if (*level_name != '\0')
6932   {
6933     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6934     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6935   }
6936   else if (*last_comment != '\0')
6937   {
6938     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6939     level->name[MAX_LEVEL_NAME_LEN] = '\0';
6940   }
6941   else
6942   {
6943     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6944   }
6945
6946   // set all empty fields beyond the border walls to invisible steel wall
6947   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6948   {
6949     if ((x == 0 || x == level->fieldx - 1 ||
6950          y == 0 || y == level->fieldy - 1) &&
6951         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6952       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6953                      level->field, level->fieldx, level->fieldy);
6954   }
6955
6956   // set special level settings for Sokoban levels
6957   SetLevelSettings_SB(level);
6958
6959   if (load_xsb_to_ces)
6960   {
6961     // special global settings can now be set in level template
6962     level->use_custom_template = TRUE;
6963   }
6964 }
6965
6966
6967 // -------------------------------------------------------------------------
6968 // functions for handling native levels
6969 // -------------------------------------------------------------------------
6970
6971 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6972                                      struct LevelFileInfo *level_file_info,
6973                                      boolean level_info_only)
6974 {
6975   int pos = 0;
6976
6977   // determine position of requested level inside level package
6978   if (level_file_info->packed)
6979     pos = level_file_info->nr - leveldir_current->first_level;
6980
6981   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6982     level->no_valid_file = TRUE;
6983 }
6984
6985 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6986                                      struct LevelFileInfo *level_file_info,
6987                                      boolean level_info_only)
6988 {
6989   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6990     level->no_valid_file = TRUE;
6991 }
6992
6993 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6994                                      struct LevelFileInfo *level_file_info,
6995                                      boolean level_info_only)
6996 {
6997   int pos = 0;
6998
6999   // determine position of requested level inside level package
7000   if (level_file_info->packed)
7001     pos = level_file_info->nr - leveldir_current->first_level;
7002
7003   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7004     level->no_valid_file = TRUE;
7005 }
7006
7007 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7008                                      struct LevelFileInfo *level_file_info,
7009                                      boolean level_info_only)
7010 {
7011   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7012     level->no_valid_file = TRUE;
7013 }
7014
7015 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7016 {
7017   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7018     CopyNativeLevel_RND_to_BD(level);
7019   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7020     CopyNativeLevel_RND_to_EM(level);
7021   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7022     CopyNativeLevel_RND_to_SP(level);
7023   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7024     CopyNativeLevel_RND_to_MM(level);
7025 }
7026
7027 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7028 {
7029   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7030     CopyNativeLevel_BD_to_RND(level);
7031   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7032     CopyNativeLevel_EM_to_RND(level);
7033   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7034     CopyNativeLevel_SP_to_RND(level);
7035   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7036     CopyNativeLevel_MM_to_RND(level);
7037 }
7038
7039 void SaveNativeLevel(struct LevelInfo *level)
7040 {
7041   // saving native level files only supported for some game engines
7042   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7043       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7044     return;
7045
7046   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7047                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7048   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7049   char *filename = getLevelFilenameFromBasename(basename);
7050
7051   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7052     return;
7053
7054   boolean success = FALSE;
7055
7056   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7057   {
7058     CopyNativeLevel_RND_to_BD(level);
7059     // CopyNativeTape_RND_to_BD(level);
7060
7061     success = SaveNativeLevel_BD(filename);
7062   }
7063   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7064   {
7065     CopyNativeLevel_RND_to_SP(level);
7066     CopyNativeTape_RND_to_SP(level);
7067
7068     success = SaveNativeLevel_SP(filename);
7069   }
7070
7071   if (success)
7072     Request("Native level file saved!", REQ_CONFIRM);
7073   else
7074     Request("Failed to save native level file!", REQ_CONFIRM);
7075 }
7076
7077
7078 // ----------------------------------------------------------------------------
7079 // functions for loading generic level
7080 // ----------------------------------------------------------------------------
7081
7082 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7083                                   struct LevelFileInfo *level_file_info,
7084                                   boolean level_info_only)
7085 {
7086   // always start with reliable default values
7087   setLevelInfoToDefaults(level, level_info_only, TRUE);
7088
7089   switch (level_file_info->type)
7090   {
7091     case LEVEL_FILE_TYPE_RND:
7092       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7093       break;
7094
7095     case LEVEL_FILE_TYPE_BD:
7096       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7097       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7098       break;
7099
7100     case LEVEL_FILE_TYPE_EM:
7101       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7102       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7103       break;
7104
7105     case LEVEL_FILE_TYPE_SP:
7106       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7107       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7108       break;
7109
7110     case LEVEL_FILE_TYPE_MM:
7111       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7112       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7113       break;
7114
7115     case LEVEL_FILE_TYPE_DC:
7116       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7117       break;
7118
7119     case LEVEL_FILE_TYPE_SB:
7120       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7121       break;
7122
7123     default:
7124       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7125       break;
7126   }
7127
7128   // if level file is invalid, restore level structure to default values
7129   if (level->no_valid_file)
7130     setLevelInfoToDefaults(level, level_info_only, FALSE);
7131
7132   if (check_special_flags("use_native_bd_game_engine"))
7133     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7134
7135   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7136     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7137
7138   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7139     CopyNativeLevel_Native_to_RND(level);
7140 }
7141
7142 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7143 {
7144   static struct LevelFileInfo level_file_info;
7145
7146   // always start with reliable default values
7147   setFileInfoToDefaults(&level_file_info);
7148
7149   level_file_info.nr = 0;                       // unknown level number
7150   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7151
7152   setString(&level_file_info.filename, filename);
7153
7154   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7155 }
7156
7157 static void LoadLevel_InitVersion(struct LevelInfo *level)
7158 {
7159   int i, j;
7160
7161   if (leveldir_current == NULL)         // only when dumping level
7162     return;
7163
7164   // all engine modifications also valid for levels which use latest engine
7165   if (level->game_version < VERSION_IDENT(3,2,0,5))
7166   {
7167     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7168     level->time_score_base = 10;
7169   }
7170
7171   if (leveldir_current->latest_engine)
7172   {
7173     // ---------- use latest game engine --------------------------------------
7174
7175     /* For all levels which are forced to use the latest game engine version
7176        (normally all but user contributed, private and undefined levels), set
7177        the game engine version to the actual version; this allows for actual
7178        corrections in the game engine to take effect for existing, converted
7179        levels (from "classic" or other existing games) to make the emulation
7180        of the corresponding game more accurate, while (hopefully) not breaking
7181        existing levels created from other players. */
7182
7183     level->game_version = GAME_VERSION_ACTUAL;
7184
7185     /* Set special EM style gems behaviour: EM style gems slip down from
7186        normal, steel and growing wall. As this is a more fundamental change,
7187        it seems better to set the default behaviour to "off" (as it is more
7188        natural) and make it configurable in the level editor (as a property
7189        of gem style elements). Already existing converted levels (neither
7190        private nor contributed levels) are changed to the new behaviour. */
7191
7192     if (level->file_version < FILE_VERSION_2_0)
7193       level->em_slippery_gems = TRUE;
7194
7195     return;
7196   }
7197
7198   // ---------- use game engine the level was created with --------------------
7199
7200   /* For all levels which are not forced to use the latest game engine
7201      version (normally user contributed, private and undefined levels),
7202      use the version of the game engine the levels were created for.
7203
7204      Since 2.0.1, the game engine version is now directly stored
7205      in the level file (chunk "VERS"), so there is no need anymore
7206      to set the game version from the file version (except for old,
7207      pre-2.0 levels, where the game version is still taken from the
7208      file format version used to store the level -- see above). */
7209
7210   // player was faster than enemies in 1.0.0 and before
7211   if (level->file_version == FILE_VERSION_1_0)
7212     for (i = 0; i < MAX_PLAYERS; i++)
7213       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7214
7215   // default behaviour for EM style gems was "slippery" only in 2.0.1
7216   if (level->game_version == VERSION_IDENT(2,0,1,0))
7217     level->em_slippery_gems = TRUE;
7218
7219   // springs could be pushed over pits before (pre-release version) 2.2.0
7220   if (level->game_version < VERSION_IDENT(2,2,0,0))
7221     level->use_spring_bug = TRUE;
7222
7223   if (level->game_version < VERSION_IDENT(3,2,0,5))
7224   {
7225     // time orb caused limited time in endless time levels before 3.2.0-5
7226     level->use_time_orb_bug = TRUE;
7227
7228     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7229     level->block_snap_field = FALSE;
7230
7231     // extra time score was same value as time left score before 3.2.0-5
7232     level->extra_time_score = level->score[SC_TIME_BONUS];
7233   }
7234
7235   if (level->game_version < VERSION_IDENT(3,2,0,7))
7236   {
7237     // default behaviour for snapping was "not continuous" before 3.2.0-7
7238     level->continuous_snapping = FALSE;
7239   }
7240
7241   // only few elements were able to actively move into acid before 3.1.0
7242   // trigger settings did not exist before 3.1.0; set to default "any"
7243   if (level->game_version < VERSION_IDENT(3,1,0,0))
7244   {
7245     // correct "can move into acid" settings (all zero in old levels)
7246
7247     level->can_move_into_acid_bits = 0; // nothing can move into acid
7248     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7249
7250     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7251     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7252     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7253     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7254
7255     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7256       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7257
7258     // correct trigger settings (stored as zero == "none" in old levels)
7259
7260     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7261     {
7262       int element = EL_CUSTOM_START + i;
7263       struct ElementInfo *ei = &element_info[element];
7264
7265       for (j = 0; j < ei->num_change_pages; j++)
7266       {
7267         struct ElementChangeInfo *change = &ei->change_page[j];
7268
7269         change->trigger_player = CH_PLAYER_ANY;
7270         change->trigger_page = CH_PAGE_ANY;
7271       }
7272     }
7273   }
7274
7275   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7276   {
7277     int element = EL_CUSTOM_256;
7278     struct ElementInfo *ei = &element_info[element];
7279     struct ElementChangeInfo *change = &ei->change_page[0];
7280
7281     /* This is needed to fix a problem that was caused by a bugfix in function
7282        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7283        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7284        not replace walkable elements, but instead just placed the player on it,
7285        without placing the Sokoban field under the player). Unfortunately, this
7286        breaks "Snake Bite" style levels when the snake is halfway through a door
7287        that just closes (the snake head is still alive and can be moved in this
7288        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7289        player (without Sokoban element) which then gets killed as designed). */
7290
7291     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7292          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7293         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7294       change->target_element = EL_PLAYER_1;
7295   }
7296
7297   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7298   if (level->game_version < VERSION_IDENT(3,2,5,0))
7299   {
7300     /* This is needed to fix a problem that was caused by a bugfix in function
7301        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7302        corrects the behaviour when a custom element changes to another custom
7303        element with a higher element number that has change actions defined.
7304        Normally, only one change per frame is allowed for custom elements.
7305        Therefore, it is checked if a custom element already changed in the
7306        current frame; if it did, subsequent changes are suppressed.
7307        Unfortunately, this is only checked for element changes, but not for
7308        change actions, which are still executed. As the function above loops
7309        through all custom elements from lower to higher, an element change
7310        resulting in a lower CE number won't be checked again, while a target
7311        element with a higher number will also be checked, and potential change
7312        actions will get executed for this CE, too (which is wrong), while
7313        further changes are ignored (which is correct). As this bugfix breaks
7314        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7315        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7316        behaviour for existing levels and tapes that make use of this bug */
7317
7318     level->use_action_after_change_bug = TRUE;
7319   }
7320
7321   // not centering level after relocating player was default only in 3.2.3
7322   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7323     level->shifted_relocation = TRUE;
7324
7325   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7326   if (level->game_version < VERSION_IDENT(3,2,6,0))
7327     level->em_explodes_by_fire = TRUE;
7328
7329   // levels were solved by the first player entering an exit up to 4.1.0.0
7330   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7331     level->solved_by_one_player = TRUE;
7332
7333   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7334   if (level->game_version < VERSION_IDENT(4,1,1,1))
7335     level->use_life_bugs = TRUE;
7336
7337   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7338   if (level->game_version < VERSION_IDENT(4,1,1,1))
7339     level->sb_objects_needed = FALSE;
7340
7341   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7342   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7343     level->finish_dig_collect = FALSE;
7344
7345   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7346   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7347     level->keep_walkable_ce = TRUE;
7348 }
7349
7350 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7351 {
7352   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7353   int x, y;
7354
7355   // check if this level is (not) a Sokoban level
7356   for (y = 0; y < level->fieldy; y++)
7357     for (x = 0; x < level->fieldx; x++)
7358       if (!IS_SB_ELEMENT(Tile[x][y]))
7359         is_sokoban_level = FALSE;
7360
7361   if (is_sokoban_level)
7362   {
7363     // set special level settings for Sokoban levels
7364     SetLevelSettings_SB(level);
7365   }
7366 }
7367
7368 static void LoadLevel_InitSettings(struct LevelInfo *level)
7369 {
7370   // adjust level settings for (non-native) Sokoban-style levels
7371   LoadLevel_InitSettings_SB(level);
7372
7373   // rename levels with title "nameless level" or if renaming is forced
7374   if (leveldir_current->empty_level_name != NULL &&
7375       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7376        leveldir_current->force_level_name))
7377     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7378              leveldir_current->empty_level_name, level_nr);
7379 }
7380
7381 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7382 {
7383   int i, x, y;
7384
7385   // map elements that have changed in newer versions
7386   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7387                                                     level->game_version);
7388   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7389     for (x = 0; x < 3; x++)
7390       for (y = 0; y < 3; y++)
7391         level->yamyam_content[i].e[x][y] =
7392           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7393                                     level->game_version);
7394
7395 }
7396
7397 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7398 {
7399   int i, j;
7400
7401   // map custom element change events that have changed in newer versions
7402   // (these following values were accidentally changed in version 3.0.1)
7403   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7404   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7405   {
7406     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7407     {
7408       int element = EL_CUSTOM_START + i;
7409
7410       // order of checking and copying events to be mapped is important
7411       // (do not change the start and end value -- they are constant)
7412       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7413       {
7414         if (HAS_CHANGE_EVENT(element, j - 2))
7415         {
7416           SET_CHANGE_EVENT(element, j - 2, FALSE);
7417           SET_CHANGE_EVENT(element, j, TRUE);
7418         }
7419       }
7420
7421       // order of checking and copying events to be mapped is important
7422       // (do not change the start and end value -- they are constant)
7423       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7424       {
7425         if (HAS_CHANGE_EVENT(element, j - 1))
7426         {
7427           SET_CHANGE_EVENT(element, j - 1, FALSE);
7428           SET_CHANGE_EVENT(element, j, TRUE);
7429         }
7430       }
7431     }
7432   }
7433
7434   // initialize "can_change" field for old levels with only one change page
7435   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7436   {
7437     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7438     {
7439       int element = EL_CUSTOM_START + i;
7440
7441       if (CAN_CHANGE(element))
7442         element_info[element].change->can_change = TRUE;
7443     }
7444   }
7445
7446   // correct custom element values (for old levels without these options)
7447   if (level->game_version < VERSION_IDENT(3,1,1,0))
7448   {
7449     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7450     {
7451       int element = EL_CUSTOM_START + i;
7452       struct ElementInfo *ei = &element_info[element];
7453
7454       if (ei->access_direction == MV_NO_DIRECTION)
7455         ei->access_direction = MV_ALL_DIRECTIONS;
7456     }
7457   }
7458
7459   // correct custom element values (fix invalid values for all versions)
7460   if (1)
7461   {
7462     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7463     {
7464       int element = EL_CUSTOM_START + i;
7465       struct ElementInfo *ei = &element_info[element];
7466
7467       for (j = 0; j < ei->num_change_pages; j++)
7468       {
7469         struct ElementChangeInfo *change = &ei->change_page[j];
7470
7471         if (change->trigger_player == CH_PLAYER_NONE)
7472           change->trigger_player = CH_PLAYER_ANY;
7473
7474         if (change->trigger_side == CH_SIDE_NONE)
7475           change->trigger_side = CH_SIDE_ANY;
7476       }
7477     }
7478   }
7479
7480   // initialize "can_explode" field for old levels which did not store this
7481   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7482   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7483   {
7484     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7485     {
7486       int element = EL_CUSTOM_START + i;
7487
7488       if (EXPLODES_1X1_OLD(element))
7489         element_info[element].explosion_type = EXPLODES_1X1;
7490
7491       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7492                                              EXPLODES_SMASHED(element) ||
7493                                              EXPLODES_IMPACT(element)));
7494     }
7495   }
7496
7497   // correct previously hard-coded move delay values for maze runner style
7498   if (level->game_version < VERSION_IDENT(3,1,1,0))
7499   {
7500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7501     {
7502       int element = EL_CUSTOM_START + i;
7503
7504       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7505       {
7506         // previously hard-coded and therefore ignored
7507         element_info[element].move_delay_fixed = 9;
7508         element_info[element].move_delay_random = 0;
7509       }
7510     }
7511   }
7512
7513   // set some other uninitialized values of custom elements in older levels
7514   if (level->game_version < VERSION_IDENT(3,1,0,0))
7515   {
7516     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7517     {
7518       int element = EL_CUSTOM_START + i;
7519
7520       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7521
7522       element_info[element].explosion_delay = 17;
7523       element_info[element].ignition_delay = 8;
7524     }
7525   }
7526
7527   // set mouse click change events to work for left/middle/right mouse button
7528   if (level->game_version < VERSION_IDENT(4,2,3,0))
7529   {
7530     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7531     {
7532       int element = EL_CUSTOM_START + i;
7533       struct ElementInfo *ei = &element_info[element];
7534
7535       for (j = 0; j < ei->num_change_pages; j++)
7536       {
7537         struct ElementChangeInfo *change = &ei->change_page[j];
7538
7539         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7540             change->has_event[CE_PRESSED_BY_MOUSE] ||
7541             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7542             change->has_event[CE_MOUSE_PRESSED_ON_X])
7543           change->trigger_side = CH_SIDE_ANY;
7544       }
7545     }
7546   }
7547 }
7548
7549 static void LoadLevel_InitElements(struct LevelInfo *level)
7550 {
7551   LoadLevel_InitStandardElements(level);
7552
7553   if (level->file_has_custom_elements)
7554     LoadLevel_InitCustomElements(level);
7555
7556   // initialize element properties for level editor etc.
7557   InitElementPropertiesEngine(level->game_version);
7558   InitElementPropertiesGfxElement();
7559 }
7560
7561 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7562 {
7563   int x, y;
7564
7565   // map elements that have changed in newer versions
7566   for (y = 0; y < level->fieldy; y++)
7567     for (x = 0; x < level->fieldx; x++)
7568       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7569                                                      level->game_version);
7570
7571   // clear unused playfield data (nicer if level gets resized in editor)
7572   for (x = 0; x < MAX_LEV_FIELDX; x++)
7573     for (y = 0; y < MAX_LEV_FIELDY; y++)
7574       if (x >= level->fieldx || y >= level->fieldy)
7575         level->field[x][y] = EL_EMPTY;
7576
7577   // copy elements to runtime playfield array
7578   for (x = 0; x < MAX_LEV_FIELDX; x++)
7579     for (y = 0; y < MAX_LEV_FIELDY; y++)
7580       Tile[x][y] = level->field[x][y];
7581
7582   // initialize level size variables for faster access
7583   lev_fieldx = level->fieldx;
7584   lev_fieldy = level->fieldy;
7585
7586   // determine border element for this level
7587   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7588     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7589   else
7590     SetBorderElement();
7591 }
7592
7593 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7594 {
7595   struct LevelFileInfo *level_file_info = &level->file_info;
7596
7597   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7598     CopyNativeLevel_RND_to_Native(level);
7599 }
7600
7601 static void LoadLevelTemplate_LoadAndInit(void)
7602 {
7603   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7604
7605   LoadLevel_InitVersion(&level_template);
7606   LoadLevel_InitElements(&level_template);
7607   LoadLevel_InitSettings(&level_template);
7608
7609   ActivateLevelTemplate();
7610 }
7611
7612 void LoadLevelTemplate(int nr)
7613 {
7614   if (!fileExists(getGlobalLevelTemplateFilename()))
7615   {
7616     Warn("no level template found for this level");
7617
7618     return;
7619   }
7620
7621   setLevelFileInfo(&level_template.file_info, nr);
7622
7623   LoadLevelTemplate_LoadAndInit();
7624 }
7625
7626 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7627 {
7628   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7629
7630   LoadLevelTemplate_LoadAndInit();
7631 }
7632
7633 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7634 {
7635   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7636
7637   if (level.use_custom_template)
7638   {
7639     if (network_level != NULL)
7640       LoadNetworkLevelTemplate(network_level);
7641     else
7642       LoadLevelTemplate(-1);
7643   }
7644
7645   LoadLevel_InitVersion(&level);
7646   LoadLevel_InitElements(&level);
7647   LoadLevel_InitPlayfield(&level);
7648   LoadLevel_InitSettings(&level);
7649
7650   LoadLevel_InitNativeEngines(&level);
7651 }
7652
7653 void LoadLevel(int nr)
7654 {
7655   SetLevelSetInfo(leveldir_current->identifier, nr);
7656
7657   setLevelFileInfo(&level.file_info, nr);
7658
7659   LoadLevel_LoadAndInit(NULL);
7660 }
7661
7662 void LoadLevelInfoOnly(int nr)
7663 {
7664   setLevelFileInfo(&level.file_info, nr);
7665
7666   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7667 }
7668
7669 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7670 {
7671   SetLevelSetInfo(network_level->leveldir_identifier,
7672                   network_level->file_info.nr);
7673
7674   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7675
7676   LoadLevel_LoadAndInit(network_level);
7677 }
7678
7679 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7680 {
7681   int chunk_size = 0;
7682
7683   chunk_size += putFileVersion(file, level->file_version);
7684   chunk_size += putFileVersion(file, level->game_version);
7685
7686   return chunk_size;
7687 }
7688
7689 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7690 {
7691   int chunk_size = 0;
7692
7693   chunk_size += putFile16BitBE(file, level->creation_date.year);
7694   chunk_size += putFile8Bit(file,    level->creation_date.month);
7695   chunk_size += putFile8Bit(file,    level->creation_date.day);
7696
7697   return chunk_size;
7698 }
7699
7700 #if ENABLE_HISTORIC_CHUNKS
7701 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7702 {
7703   int i, x, y;
7704
7705   putFile8Bit(file, level->fieldx);
7706   putFile8Bit(file, level->fieldy);
7707
7708   putFile16BitBE(file, level->time);
7709   putFile16BitBE(file, level->gems_needed);
7710
7711   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7712     putFile8Bit(file, level->name[i]);
7713
7714   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7715     putFile8Bit(file, level->score[i]);
7716
7717   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7718     for (y = 0; y < 3; y++)
7719       for (x = 0; x < 3; x++)
7720         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7721                            level->yamyam_content[i].e[x][y]));
7722   putFile8Bit(file, level->amoeba_speed);
7723   putFile8Bit(file, level->time_magic_wall);
7724   putFile8Bit(file, level->time_wheel);
7725   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7726                      level->amoeba_content));
7727   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7728   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7729   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7730   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7731
7732   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7733
7734   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7735   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7736   putFile32BitBE(file, level->can_move_into_acid_bits);
7737   putFile8Bit(file, level->dont_collide_with_bits);
7738
7739   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7740   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7741
7742   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7743   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7744   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7745
7746   putFile8Bit(file, level->game_engine_type);
7747
7748   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7749 }
7750 #endif
7751
7752 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7753 {
7754   int chunk_size = 0;
7755   int i;
7756
7757   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7758     chunk_size += putFile8Bit(file, level->name[i]);
7759
7760   return chunk_size;
7761 }
7762
7763 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7764 {
7765   int chunk_size = 0;
7766   int i;
7767
7768   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7769     chunk_size += putFile8Bit(file, level->author[i]);
7770
7771   return chunk_size;
7772 }
7773
7774 #if ENABLE_HISTORIC_CHUNKS
7775 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7776 {
7777   int chunk_size = 0;
7778   int x, y;
7779
7780   for (y = 0; y < level->fieldy; y++)
7781     for (x = 0; x < level->fieldx; x++)
7782       if (level->encoding_16bit_field)
7783         chunk_size += putFile16BitBE(file, level->field[x][y]);
7784       else
7785         chunk_size += putFile8Bit(file, level->field[x][y]);
7786
7787   return chunk_size;
7788 }
7789 #endif
7790
7791 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7792 {
7793   int chunk_size = 0;
7794   int x, y;
7795
7796   for (y = 0; y < level->fieldy; y++) 
7797     for (x = 0; x < level->fieldx; x++) 
7798       chunk_size += putFile16BitBE(file, level->field[x][y]);
7799
7800   return chunk_size;
7801 }
7802
7803 #if ENABLE_HISTORIC_CHUNKS
7804 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7805 {
7806   int i, x, y;
7807
7808   putFile8Bit(file, EL_YAMYAM);
7809   putFile8Bit(file, level->num_yamyam_contents);
7810   putFile8Bit(file, 0);
7811   putFile8Bit(file, 0);
7812
7813   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7814     for (y = 0; y < 3; y++)
7815       for (x = 0; x < 3; x++)
7816         if (level->encoding_16bit_field)
7817           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7818         else
7819           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7820 }
7821 #endif
7822
7823 #if ENABLE_HISTORIC_CHUNKS
7824 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7825 {
7826   int i, x, y;
7827   int num_contents, content_xsize, content_ysize;
7828   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7829
7830   if (element == EL_YAMYAM)
7831   {
7832     num_contents = level->num_yamyam_contents;
7833     content_xsize = 3;
7834     content_ysize = 3;
7835
7836     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7837       for (y = 0; y < 3; y++)
7838         for (x = 0; x < 3; x++)
7839           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7840   }
7841   else if (element == EL_BD_AMOEBA)
7842   {
7843     num_contents = 1;
7844     content_xsize = 1;
7845     content_ysize = 1;
7846
7847     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7848       for (y = 0; y < 3; y++)
7849         for (x = 0; x < 3; x++)
7850           content_array[i][x][y] = EL_EMPTY;
7851     content_array[0][0][0] = level->amoeba_content;
7852   }
7853   else
7854   {
7855     // chunk header already written -- write empty chunk data
7856     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7857
7858     Warn("cannot save content for element '%d'", element);
7859
7860     return;
7861   }
7862
7863   putFile16BitBE(file, element);
7864   putFile8Bit(file, num_contents);
7865   putFile8Bit(file, content_xsize);
7866   putFile8Bit(file, content_ysize);
7867
7868   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7869
7870   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7871     for (y = 0; y < 3; y++)
7872       for (x = 0; x < 3; x++)
7873         putFile16BitBE(file, content_array[i][x][y]);
7874 }
7875 #endif
7876
7877 #if ENABLE_HISTORIC_CHUNKS
7878 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7879 {
7880   int envelope_nr = element - EL_ENVELOPE_1;
7881   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7882   int chunk_size = 0;
7883   int i;
7884
7885   chunk_size += putFile16BitBE(file, element);
7886   chunk_size += putFile16BitBE(file, envelope_len);
7887   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7888   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7889
7890   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7891   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7892
7893   for (i = 0; i < envelope_len; i++)
7894     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7895
7896   return chunk_size;
7897 }
7898 #endif
7899
7900 #if ENABLE_HISTORIC_CHUNKS
7901 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7902                            int num_changed_custom_elements)
7903 {
7904   int i, check = 0;
7905
7906   putFile16BitBE(file, num_changed_custom_elements);
7907
7908   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7909   {
7910     int element = EL_CUSTOM_START + i;
7911
7912     struct ElementInfo *ei = &element_info[element];
7913
7914     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7915     {
7916       if (check < num_changed_custom_elements)
7917       {
7918         putFile16BitBE(file, element);
7919         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7920       }
7921
7922       check++;
7923     }
7924   }
7925
7926   if (check != num_changed_custom_elements)     // should not happen
7927     Warn("inconsistent number of custom element properties");
7928 }
7929 #endif
7930
7931 #if ENABLE_HISTORIC_CHUNKS
7932 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7933                            int num_changed_custom_elements)
7934 {
7935   int i, check = 0;
7936
7937   putFile16BitBE(file, num_changed_custom_elements);
7938
7939   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7940   {
7941     int element = EL_CUSTOM_START + i;
7942
7943     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7944     {
7945       if (check < num_changed_custom_elements)
7946       {
7947         putFile16BitBE(file, element);
7948         putFile16BitBE(file, element_info[element].change->target_element);
7949       }
7950
7951       check++;
7952     }
7953   }
7954
7955   if (check != num_changed_custom_elements)     // should not happen
7956     Warn("inconsistent number of custom target elements");
7957 }
7958 #endif
7959
7960 #if ENABLE_HISTORIC_CHUNKS
7961 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7962                            int num_changed_custom_elements)
7963 {
7964   int i, j, x, y, check = 0;
7965
7966   putFile16BitBE(file, num_changed_custom_elements);
7967
7968   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7969   {
7970     int element = EL_CUSTOM_START + i;
7971     struct ElementInfo *ei = &element_info[element];
7972
7973     if (ei->modified_settings)
7974     {
7975       if (check < num_changed_custom_elements)
7976       {
7977         putFile16BitBE(file, element);
7978
7979         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7980           putFile8Bit(file, ei->description[j]);
7981
7982         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7983
7984         // some free bytes for future properties and padding
7985         WriteUnusedBytesToFile(file, 7);
7986
7987         putFile8Bit(file, ei->use_gfx_element);
7988         putFile16BitBE(file, ei->gfx_element_initial);
7989
7990         putFile8Bit(file, ei->collect_score_initial);
7991         putFile8Bit(file, ei->collect_count_initial);
7992
7993         putFile16BitBE(file, ei->push_delay_fixed);
7994         putFile16BitBE(file, ei->push_delay_random);
7995         putFile16BitBE(file, ei->move_delay_fixed);
7996         putFile16BitBE(file, ei->move_delay_random);
7997
7998         putFile16BitBE(file, ei->move_pattern);
7999         putFile8Bit(file, ei->move_direction_initial);
8000         putFile8Bit(file, ei->move_stepsize);
8001
8002         for (y = 0; y < 3; y++)
8003           for (x = 0; x < 3; x++)
8004             putFile16BitBE(file, ei->content.e[x][y]);
8005
8006         putFile32BitBE(file, ei->change->events);
8007
8008         putFile16BitBE(file, ei->change->target_element);
8009
8010         putFile16BitBE(file, ei->change->delay_fixed);
8011         putFile16BitBE(file, ei->change->delay_random);
8012         putFile16BitBE(file, ei->change->delay_frames);
8013
8014         putFile16BitBE(file, ei->change->initial_trigger_element);
8015
8016         putFile8Bit(file, ei->change->explode);
8017         putFile8Bit(file, ei->change->use_target_content);
8018         putFile8Bit(file, ei->change->only_if_complete);
8019         putFile8Bit(file, ei->change->use_random_replace);
8020
8021         putFile8Bit(file, ei->change->random_percentage);
8022         putFile8Bit(file, ei->change->replace_when);
8023
8024         for (y = 0; y < 3; y++)
8025           for (x = 0; x < 3; x++)
8026             putFile16BitBE(file, ei->change->content.e[x][y]);
8027
8028         putFile8Bit(file, ei->slippery_type);
8029
8030         // some free bytes for future properties and padding
8031         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8032       }
8033
8034       check++;
8035     }
8036   }
8037
8038   if (check != num_changed_custom_elements)     // should not happen
8039     Warn("inconsistent number of custom element properties");
8040 }
8041 #endif
8042
8043 #if ENABLE_HISTORIC_CHUNKS
8044 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8045 {
8046   struct ElementInfo *ei = &element_info[element];
8047   int i, j, x, y;
8048
8049   // ---------- custom element base property values (96 bytes) ----------------
8050
8051   putFile16BitBE(file, element);
8052
8053   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8054     putFile8Bit(file, ei->description[i]);
8055
8056   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8057
8058   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8059
8060   putFile8Bit(file, ei->num_change_pages);
8061
8062   putFile16BitBE(file, ei->ce_value_fixed_initial);
8063   putFile16BitBE(file, ei->ce_value_random_initial);
8064   putFile8Bit(file, ei->use_last_ce_value);
8065
8066   putFile8Bit(file, ei->use_gfx_element);
8067   putFile16BitBE(file, ei->gfx_element_initial);
8068
8069   putFile8Bit(file, ei->collect_score_initial);
8070   putFile8Bit(file, ei->collect_count_initial);
8071
8072   putFile8Bit(file, ei->drop_delay_fixed);
8073   putFile8Bit(file, ei->push_delay_fixed);
8074   putFile8Bit(file, ei->drop_delay_random);
8075   putFile8Bit(file, ei->push_delay_random);
8076   putFile16BitBE(file, ei->move_delay_fixed);
8077   putFile16BitBE(file, ei->move_delay_random);
8078
8079   // bits 0 - 15 of "move_pattern" ...
8080   putFile16BitBE(file, ei->move_pattern & 0xffff);
8081   putFile8Bit(file, ei->move_direction_initial);
8082   putFile8Bit(file, ei->move_stepsize);
8083
8084   putFile8Bit(file, ei->slippery_type);
8085
8086   for (y = 0; y < 3; y++)
8087     for (x = 0; x < 3; x++)
8088       putFile16BitBE(file, ei->content.e[x][y]);
8089
8090   putFile16BitBE(file, ei->move_enter_element);
8091   putFile16BitBE(file, ei->move_leave_element);
8092   putFile8Bit(file, ei->move_leave_type);
8093
8094   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8095   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8096
8097   putFile8Bit(file, ei->access_direction);
8098
8099   putFile8Bit(file, ei->explosion_delay);
8100   putFile8Bit(file, ei->ignition_delay);
8101   putFile8Bit(file, ei->explosion_type);
8102
8103   // some free bytes for future custom property values and padding
8104   WriteUnusedBytesToFile(file, 1);
8105
8106   // ---------- change page property values (48 bytes) ------------------------
8107
8108   for (i = 0; i < ei->num_change_pages; i++)
8109   {
8110     struct ElementChangeInfo *change = &ei->change_page[i];
8111     unsigned int event_bits;
8112
8113     // bits 0 - 31 of "has_event[]" ...
8114     event_bits = 0;
8115     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8116       if (change->has_event[j])
8117         event_bits |= (1u << j);
8118     putFile32BitBE(file, event_bits);
8119
8120     putFile16BitBE(file, change->target_element);
8121
8122     putFile16BitBE(file, change->delay_fixed);
8123     putFile16BitBE(file, change->delay_random);
8124     putFile16BitBE(file, change->delay_frames);
8125
8126     putFile16BitBE(file, change->initial_trigger_element);
8127
8128     putFile8Bit(file, change->explode);
8129     putFile8Bit(file, change->use_target_content);
8130     putFile8Bit(file, change->only_if_complete);
8131     putFile8Bit(file, change->use_random_replace);
8132
8133     putFile8Bit(file, change->random_percentage);
8134     putFile8Bit(file, change->replace_when);
8135
8136     for (y = 0; y < 3; y++)
8137       for (x = 0; x < 3; x++)
8138         putFile16BitBE(file, change->target_content.e[x][y]);
8139
8140     putFile8Bit(file, change->can_change);
8141
8142     putFile8Bit(file, change->trigger_side);
8143
8144     putFile8Bit(file, change->trigger_player);
8145     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8146                        log_2(change->trigger_page)));
8147
8148     putFile8Bit(file, change->has_action);
8149     putFile8Bit(file, change->action_type);
8150     putFile8Bit(file, change->action_mode);
8151     putFile16BitBE(file, change->action_arg);
8152
8153     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8154     event_bits = 0;
8155     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8156       if (change->has_event[j])
8157         event_bits |= (1u << (j - 32));
8158     putFile8Bit(file, event_bits);
8159   }
8160 }
8161 #endif
8162
8163 #if ENABLE_HISTORIC_CHUNKS
8164 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8165 {
8166   struct ElementInfo *ei = &element_info[element];
8167   struct ElementGroupInfo *group = ei->group;
8168   int i;
8169
8170   putFile16BitBE(file, element);
8171
8172   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8173     putFile8Bit(file, ei->description[i]);
8174
8175   putFile8Bit(file, group->num_elements);
8176
8177   putFile8Bit(file, ei->use_gfx_element);
8178   putFile16BitBE(file, ei->gfx_element_initial);
8179
8180   putFile8Bit(file, group->choice_mode);
8181
8182   // some free bytes for future values and padding
8183   WriteUnusedBytesToFile(file, 3);
8184
8185   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8186     putFile16BitBE(file, group->element[i]);
8187 }
8188 #endif
8189
8190 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8191                                 boolean write_element)
8192 {
8193   int save_type = entry->save_type;
8194   int data_type = entry->data_type;
8195   int conf_type = entry->conf_type;
8196   int byte_mask = conf_type & CONF_MASK_BYTES;
8197   int element = entry->element;
8198   int default_value = entry->default_value;
8199   int num_bytes = 0;
8200   boolean modified = FALSE;
8201
8202   if (byte_mask != CONF_MASK_MULTI_BYTES)
8203   {
8204     void *value_ptr = entry->value;
8205     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8206                  *(int *)value_ptr);
8207
8208     // check if any settings have been modified before saving them
8209     if (value != default_value)
8210       modified = TRUE;
8211
8212     // do not save if explicitly told or if unmodified default settings
8213     if ((save_type == SAVE_CONF_NEVER) ||
8214         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8215       return 0;
8216
8217     if (write_element)
8218       num_bytes += putFile16BitBE(file, element);
8219
8220     num_bytes += putFile8Bit(file, conf_type);
8221     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8222                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8223                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8224                   0);
8225   }
8226   else if (data_type == TYPE_STRING)
8227   {
8228     char *default_string = entry->default_string;
8229     char *string = (char *)(entry->value);
8230     int string_length = strlen(string);
8231     int i;
8232
8233     // check if any settings have been modified before saving them
8234     if (!strEqual(string, default_string))
8235       modified = TRUE;
8236
8237     // do not save if explicitly told or if unmodified default settings
8238     if ((save_type == SAVE_CONF_NEVER) ||
8239         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8240       return 0;
8241
8242     if (write_element)
8243       num_bytes += putFile16BitBE(file, element);
8244
8245     num_bytes += putFile8Bit(file, conf_type);
8246     num_bytes += putFile16BitBE(file, string_length);
8247
8248     for (i = 0; i < string_length; i++)
8249       num_bytes += putFile8Bit(file, string[i]);
8250   }
8251   else if (data_type == TYPE_ELEMENT_LIST)
8252   {
8253     int *element_array = (int *)(entry->value);
8254     int num_elements = *(int *)(entry->num_entities);
8255     int i;
8256
8257     // check if any settings have been modified before saving them
8258     for (i = 0; i < num_elements; i++)
8259       if (element_array[i] != default_value)
8260         modified = TRUE;
8261
8262     // do not save if explicitly told or if unmodified default settings
8263     if ((save_type == SAVE_CONF_NEVER) ||
8264         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8265       return 0;
8266
8267     if (write_element)
8268       num_bytes += putFile16BitBE(file, element);
8269
8270     num_bytes += putFile8Bit(file, conf_type);
8271     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8272
8273     for (i = 0; i < num_elements; i++)
8274       num_bytes += putFile16BitBE(file, element_array[i]);
8275   }
8276   else if (data_type == TYPE_CONTENT_LIST)
8277   {
8278     struct Content *content = (struct Content *)(entry->value);
8279     int num_contents = *(int *)(entry->num_entities);
8280     int i, x, y;
8281
8282     // check if any settings have been modified before saving them
8283     for (i = 0; i < num_contents; i++)
8284       for (y = 0; y < 3; y++)
8285         for (x = 0; x < 3; x++)
8286           if (content[i].e[x][y] != default_value)
8287             modified = TRUE;
8288
8289     // do not save if explicitly told or if unmodified default settings
8290     if ((save_type == SAVE_CONF_NEVER) ||
8291         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8292       return 0;
8293
8294     if (write_element)
8295       num_bytes += putFile16BitBE(file, element);
8296
8297     num_bytes += putFile8Bit(file, conf_type);
8298     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8299
8300     for (i = 0; i < num_contents; i++)
8301       for (y = 0; y < 3; y++)
8302         for (x = 0; x < 3; x++)
8303           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8304   }
8305
8306   return num_bytes;
8307 }
8308
8309 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8310 {
8311   int chunk_size = 0;
8312   int i;
8313
8314   li = *level;          // copy level data into temporary buffer
8315
8316   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8317     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8318
8319   return chunk_size;
8320 }
8321
8322 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8323 {
8324   int chunk_size = 0;
8325   int i;
8326
8327   li = *level;          // copy level data into temporary buffer
8328
8329   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8330     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8331
8332   return chunk_size;
8333 }
8334
8335 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8336 {
8337   int envelope_nr = element - EL_ENVELOPE_1;
8338   int chunk_size = 0;
8339   int i;
8340
8341   chunk_size += putFile16BitBE(file, element);
8342
8343   // copy envelope data into temporary buffer
8344   xx_envelope = level->envelope[envelope_nr];
8345
8346   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8347     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8348
8349   return chunk_size;
8350 }
8351
8352 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8353 {
8354   struct ElementInfo *ei = &element_info[element];
8355   int chunk_size = 0;
8356   int i, j;
8357
8358   chunk_size += putFile16BitBE(file, element);
8359
8360   xx_ei = *ei;          // copy element data into temporary buffer
8361
8362   // set default description string for this specific element
8363   strcpy(xx_default_description, getDefaultElementDescription(ei));
8364
8365   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8366     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8367
8368   for (i = 0; i < ei->num_change_pages; i++)
8369   {
8370     struct ElementChangeInfo *change = &ei->change_page[i];
8371
8372     xx_current_change_page = i;
8373
8374     xx_change = *change;        // copy change data into temporary buffer
8375
8376     resetEventBits();
8377     setEventBitsFromEventFlags(change);
8378
8379     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8380       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8381                                          FALSE);
8382   }
8383
8384   return chunk_size;
8385 }
8386
8387 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8388 {
8389   struct ElementInfo *ei = &element_info[element];
8390   struct ElementGroupInfo *group = ei->group;
8391   int chunk_size = 0;
8392   int i;
8393
8394   chunk_size += putFile16BitBE(file, element);
8395
8396   xx_ei = *ei;          // copy element data into temporary buffer
8397   xx_group = *group;    // copy group data into temporary buffer
8398
8399   // set default description string for this specific element
8400   strcpy(xx_default_description, getDefaultElementDescription(ei));
8401
8402   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8403     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8404
8405   return chunk_size;
8406 }
8407
8408 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8409 {
8410   struct ElementInfo *ei = &element_info[element];
8411   int chunk_size = 0;
8412   int i;
8413
8414   chunk_size += putFile16BitBE(file, element);
8415
8416   xx_ei = *ei;          // copy element data into temporary buffer
8417
8418   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8419     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8420
8421   return chunk_size;
8422 }
8423
8424 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8425                                   boolean save_as_template)
8426 {
8427   int chunk_size;
8428   int i;
8429   FILE *file;
8430
8431   if (!(file = fopen(filename, MODE_WRITE)))
8432   {
8433     Warn("cannot save level file '%s'", filename);
8434
8435     return;
8436   }
8437
8438   level->file_version = FILE_VERSION_ACTUAL;
8439   level->game_version = GAME_VERSION_ACTUAL;
8440
8441   level->creation_date = getCurrentDate();
8442
8443   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8444   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8445
8446   chunk_size = SaveLevel_VERS(NULL, level);
8447   putFileChunkBE(file, "VERS", chunk_size);
8448   SaveLevel_VERS(file, level);
8449
8450   chunk_size = SaveLevel_DATE(NULL, level);
8451   putFileChunkBE(file, "DATE", chunk_size);
8452   SaveLevel_DATE(file, level);
8453
8454   chunk_size = SaveLevel_NAME(NULL, level);
8455   putFileChunkBE(file, "NAME", chunk_size);
8456   SaveLevel_NAME(file, level);
8457
8458   chunk_size = SaveLevel_AUTH(NULL, level);
8459   putFileChunkBE(file, "AUTH", chunk_size);
8460   SaveLevel_AUTH(file, level);
8461
8462   chunk_size = SaveLevel_INFO(NULL, level);
8463   putFileChunkBE(file, "INFO", chunk_size);
8464   SaveLevel_INFO(file, level);
8465
8466   chunk_size = SaveLevel_BODY(NULL, level);
8467   putFileChunkBE(file, "BODY", chunk_size);
8468   SaveLevel_BODY(file, level);
8469
8470   chunk_size = SaveLevel_ELEM(NULL, level);
8471   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8472   {
8473     putFileChunkBE(file, "ELEM", chunk_size);
8474     SaveLevel_ELEM(file, level);
8475   }
8476
8477   for (i = 0; i < NUM_ENVELOPES; i++)
8478   {
8479     int element = EL_ENVELOPE_1 + i;
8480
8481     chunk_size = SaveLevel_NOTE(NULL, level, element);
8482     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8483     {
8484       putFileChunkBE(file, "NOTE", chunk_size);
8485       SaveLevel_NOTE(file, level, element);
8486     }
8487   }
8488
8489   // if not using template level, check for non-default custom/group elements
8490   if (!level->use_custom_template || save_as_template)
8491   {
8492     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8493     {
8494       int element = EL_CUSTOM_START + i;
8495
8496       chunk_size = SaveLevel_CUSX(NULL, level, element);
8497       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8498       {
8499         putFileChunkBE(file, "CUSX", chunk_size);
8500         SaveLevel_CUSX(file, level, element);
8501       }
8502     }
8503
8504     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8505     {
8506       int element = EL_GROUP_START + i;
8507
8508       chunk_size = SaveLevel_GRPX(NULL, level, element);
8509       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8510       {
8511         putFileChunkBE(file, "GRPX", chunk_size);
8512         SaveLevel_GRPX(file, level, element);
8513       }
8514     }
8515
8516     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8517     {
8518       int element = GET_EMPTY_ELEMENT(i);
8519
8520       chunk_size = SaveLevel_EMPX(NULL, level, element);
8521       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8522       {
8523         putFileChunkBE(file, "EMPX", chunk_size);
8524         SaveLevel_EMPX(file, level, element);
8525       }
8526     }
8527   }
8528
8529   fclose(file);
8530
8531   SetFilePermissions(filename, PERMS_PRIVATE);
8532 }
8533
8534 void SaveLevel(int nr)
8535 {
8536   char *filename = getDefaultLevelFilename(nr);
8537
8538   SaveLevelFromFilename(&level, filename, FALSE);
8539 }
8540
8541 void SaveLevelTemplate(void)
8542 {
8543   char *filename = getLocalLevelTemplateFilename();
8544
8545   SaveLevelFromFilename(&level, filename, TRUE);
8546 }
8547
8548 boolean SaveLevelChecked(int nr)
8549 {
8550   char *filename = getDefaultLevelFilename(nr);
8551   boolean new_level = !fileExists(filename);
8552   boolean level_saved = FALSE;
8553
8554   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8555   {
8556     SaveLevel(nr);
8557
8558     if (new_level)
8559       Request("Level saved!", REQ_CONFIRM);
8560
8561     level_saved = TRUE;
8562   }
8563
8564   return level_saved;
8565 }
8566
8567 void DumpLevel(struct LevelInfo *level)
8568 {
8569   if (level->no_level_file || level->no_valid_file)
8570   {
8571     Warn("cannot dump -- no valid level file found");
8572
8573     return;
8574   }
8575
8576   PrintLine("-", 79);
8577   Print("Level xxx (file version %08d, game version %08d)\n",
8578         level->file_version, level->game_version);
8579   PrintLine("-", 79);
8580
8581   Print("Level author: '%s'\n", level->author);
8582   Print("Level title:  '%s'\n", level->name);
8583   Print("\n");
8584   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8585   Print("\n");
8586   Print("Level time:  %d seconds\n", level->time);
8587   Print("Gems needed: %d\n", level->gems_needed);
8588   Print("\n");
8589   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8590   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8591   Print("Time for light:      %d seconds\n", level->time_light);
8592   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8593   Print("\n");
8594   Print("Amoeba speed: %d\n", level->amoeba_speed);
8595   Print("\n");
8596
8597   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8598   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8599   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8600   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8601   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8602   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8603
8604   if (options.debug)
8605   {
8606     int i, j;
8607
8608     for (i = 0; i < NUM_ENVELOPES; i++)
8609     {
8610       char *text = level->envelope[i].text;
8611       int text_len = strlen(text);
8612       boolean has_text = FALSE;
8613
8614       for (j = 0; j < text_len; j++)
8615         if (text[j] != ' ' && text[j] != '\n')
8616           has_text = TRUE;
8617
8618       if (has_text)
8619       {
8620         Print("\n");
8621         Print("Envelope %d:\n'%s'\n", i + 1, text);
8622       }
8623     }
8624   }
8625
8626   PrintLine("-", 79);
8627 }
8628
8629 void DumpLevels(void)
8630 {
8631   static LevelDirTree *dumplevel_leveldir = NULL;
8632
8633   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8634                                                  global.dumplevel_leveldir);
8635
8636   if (dumplevel_leveldir == NULL)
8637     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8638
8639   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8640       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8641     Fail("no such level number: %d", global.dumplevel_level_nr);
8642
8643   leveldir_current = dumplevel_leveldir;
8644
8645   LoadLevel(global.dumplevel_level_nr);
8646   DumpLevel(&level);
8647
8648   CloseAllAndExit(0);
8649 }
8650
8651
8652 // ============================================================================
8653 // tape file functions
8654 // ============================================================================
8655
8656 static void setTapeInfoToDefaults(void)
8657 {
8658   int i;
8659
8660   // always start with reliable default values (empty tape)
8661   TapeErase();
8662
8663   // default values (also for pre-1.2 tapes) with only the first player
8664   tape.player_participates[0] = TRUE;
8665   for (i = 1; i < MAX_PLAYERS; i++)
8666     tape.player_participates[i] = FALSE;
8667
8668   // at least one (default: the first) player participates in every tape
8669   tape.num_participating_players = 1;
8670
8671   tape.property_bits = TAPE_PROPERTY_NONE;
8672
8673   tape.level_nr = level_nr;
8674   tape.counter = 0;
8675   tape.changed = FALSE;
8676   tape.solved = FALSE;
8677
8678   tape.recording = FALSE;
8679   tape.playing = FALSE;
8680   tape.pausing = FALSE;
8681
8682   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8683   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8684
8685   tape.no_info_chunk = TRUE;
8686   tape.no_valid_file = FALSE;
8687 }
8688
8689 static int getTapePosSize(struct TapeInfo *tape)
8690 {
8691   int tape_pos_size = 0;
8692
8693   if (tape->use_key_actions)
8694     tape_pos_size += tape->num_participating_players;
8695
8696   if (tape->use_mouse_actions)
8697     tape_pos_size += 3;         // x and y position and mouse button mask
8698
8699   tape_pos_size += 1;           // tape action delay value
8700
8701   return tape_pos_size;
8702 }
8703
8704 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8705 {
8706   tape->use_key_actions = FALSE;
8707   tape->use_mouse_actions = FALSE;
8708
8709   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8710     tape->use_key_actions = TRUE;
8711
8712   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8713     tape->use_mouse_actions = TRUE;
8714 }
8715
8716 static int getTapeActionValue(struct TapeInfo *tape)
8717 {
8718   return (tape->use_key_actions &&
8719           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8720           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8721           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8722           TAPE_ACTIONS_DEFAULT);
8723 }
8724
8725 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8726 {
8727   tape->file_version = getFileVersion(file);
8728   tape->game_version = getFileVersion(file);
8729
8730   return chunk_size;
8731 }
8732
8733 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8734 {
8735   int i;
8736
8737   tape->random_seed = getFile32BitBE(file);
8738   tape->date        = getFile32BitBE(file);
8739   tape->length      = getFile32BitBE(file);
8740
8741   // read header fields that are new since version 1.2
8742   if (tape->file_version >= FILE_VERSION_1_2)
8743   {
8744     byte store_participating_players = getFile8Bit(file);
8745     int engine_version;
8746
8747     // since version 1.2, tapes store which players participate in the tape
8748     tape->num_participating_players = 0;
8749     for (i = 0; i < MAX_PLAYERS; i++)
8750     {
8751       tape->player_participates[i] = FALSE;
8752
8753       if (store_participating_players & (1 << i))
8754       {
8755         tape->player_participates[i] = TRUE;
8756         tape->num_participating_players++;
8757       }
8758     }
8759
8760     setTapeActionFlags(tape, getFile8Bit(file));
8761
8762     tape->property_bits = getFile8Bit(file);
8763     tape->solved = getFile8Bit(file);
8764
8765     engine_version = getFileVersion(file);
8766     if (engine_version > 0)
8767       tape->engine_version = engine_version;
8768     else
8769       tape->engine_version = tape->game_version;
8770   }
8771
8772   return chunk_size;
8773 }
8774
8775 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8776 {
8777   tape->scr_fieldx = getFile8Bit(file);
8778   tape->scr_fieldy = getFile8Bit(file);
8779
8780   return chunk_size;
8781 }
8782
8783 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8784 {
8785   char *level_identifier = NULL;
8786   int level_identifier_size;
8787   int i;
8788
8789   tape->no_info_chunk = FALSE;
8790
8791   level_identifier_size = getFile16BitBE(file);
8792
8793   level_identifier = checked_malloc(level_identifier_size);
8794
8795   for (i = 0; i < level_identifier_size; i++)
8796     level_identifier[i] = getFile8Bit(file);
8797
8798   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8799   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8800
8801   checked_free(level_identifier);
8802
8803   tape->level_nr = getFile16BitBE(file);
8804
8805   chunk_size = 2 + level_identifier_size + 2;
8806
8807   return chunk_size;
8808 }
8809
8810 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8811 {
8812   int i, j;
8813   int tape_pos_size = getTapePosSize(tape);
8814   int chunk_size_expected = tape_pos_size * tape->length;
8815
8816   if (chunk_size_expected != chunk_size)
8817   {
8818     ReadUnusedBytesFromFile(file, chunk_size);
8819     return chunk_size_expected;
8820   }
8821
8822   for (i = 0; i < tape->length; i++)
8823   {
8824     if (i >= MAX_TAPE_LEN)
8825     {
8826       Warn("tape truncated -- size exceeds maximum tape size %d",
8827             MAX_TAPE_LEN);
8828
8829       // tape too large; read and ignore remaining tape data from this chunk
8830       for (;i < tape->length; i++)
8831         ReadUnusedBytesFromFile(file, tape_pos_size);
8832
8833       break;
8834     }
8835
8836     if (tape->use_key_actions)
8837     {
8838       for (j = 0; j < MAX_PLAYERS; j++)
8839       {
8840         tape->pos[i].action[j] = MV_NONE;
8841
8842         if (tape->player_participates[j])
8843           tape->pos[i].action[j] = getFile8Bit(file);
8844       }
8845     }
8846
8847     if (tape->use_mouse_actions)
8848     {
8849       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8850       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8851       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8852     }
8853
8854     tape->pos[i].delay = getFile8Bit(file);
8855
8856     if (tape->file_version == FILE_VERSION_1_0)
8857     {
8858       // eliminate possible diagonal moves in old tapes
8859       // this is only for backward compatibility
8860
8861       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8862       byte action = tape->pos[i].action[0];
8863       int k, num_moves = 0;
8864
8865       for (k = 0; k < 4; k++)
8866       {
8867         if (action & joy_dir[k])
8868         {
8869           tape->pos[i + num_moves].action[0] = joy_dir[k];
8870           if (num_moves > 0)
8871             tape->pos[i + num_moves].delay = 0;
8872           num_moves++;
8873         }
8874       }
8875
8876       if (num_moves > 1)
8877       {
8878         num_moves--;
8879         i += num_moves;
8880         tape->length += num_moves;
8881       }
8882     }
8883     else if (tape->file_version < FILE_VERSION_2_0)
8884     {
8885       // convert pre-2.0 tapes to new tape format
8886
8887       if (tape->pos[i].delay > 1)
8888       {
8889         // action part
8890         tape->pos[i + 1] = tape->pos[i];
8891         tape->pos[i + 1].delay = 1;
8892
8893         // delay part
8894         for (j = 0; j < MAX_PLAYERS; j++)
8895           tape->pos[i].action[j] = MV_NONE;
8896         tape->pos[i].delay--;
8897
8898         i++;
8899         tape->length++;
8900       }
8901     }
8902
8903     if (checkEndOfFile(file))
8904       break;
8905   }
8906
8907   if (i != tape->length)
8908     chunk_size = tape_pos_size * i;
8909
8910   return chunk_size;
8911 }
8912
8913 static void LoadTape_SokobanSolution(char *filename)
8914 {
8915   File *file;
8916   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8917
8918   if (!(file = openFile(filename, MODE_READ)))
8919   {
8920     tape.no_valid_file = TRUE;
8921
8922     return;
8923   }
8924
8925   while (!checkEndOfFile(file))
8926   {
8927     unsigned char c = getByteFromFile(file);
8928
8929     if (checkEndOfFile(file))
8930       break;
8931
8932     switch (c)
8933     {
8934       case 'u':
8935       case 'U':
8936         tape.pos[tape.length].action[0] = MV_UP;
8937         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8938         tape.length++;
8939         break;
8940
8941       case 'd':
8942       case 'D':
8943         tape.pos[tape.length].action[0] = MV_DOWN;
8944         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8945         tape.length++;
8946         break;
8947
8948       case 'l':
8949       case 'L':
8950         tape.pos[tape.length].action[0] = MV_LEFT;
8951         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8952         tape.length++;
8953         break;
8954
8955       case 'r':
8956       case 'R':
8957         tape.pos[tape.length].action[0] = MV_RIGHT;
8958         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8959         tape.length++;
8960         break;
8961
8962       case '\n':
8963       case '\r':
8964       case '\t':
8965       case ' ':
8966         // ignore white-space characters
8967         break;
8968
8969       default:
8970         tape.no_valid_file = TRUE;
8971
8972         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8973
8974         break;
8975     }
8976   }
8977
8978   closeFile(file);
8979
8980   if (tape.no_valid_file)
8981     return;
8982
8983   tape.length_frames  = GetTapeLengthFrames();
8984   tape.length_seconds = GetTapeLengthSeconds();
8985 }
8986
8987 void LoadTapeFromFilename(char *filename)
8988 {
8989   char cookie[MAX_LINE_LEN];
8990   char chunk_name[CHUNK_ID_LEN + 1];
8991   File *file;
8992   int chunk_size;
8993
8994   // always start with reliable default values
8995   setTapeInfoToDefaults();
8996
8997   if (strSuffix(filename, ".sln"))
8998   {
8999     LoadTape_SokobanSolution(filename);
9000
9001     return;
9002   }
9003
9004   if (!(file = openFile(filename, MODE_READ)))
9005   {
9006     tape.no_valid_file = TRUE;
9007
9008     return;
9009   }
9010
9011   getFileChunkBE(file, chunk_name, NULL);
9012   if (strEqual(chunk_name, "RND1"))
9013   {
9014     getFile32BitBE(file);               // not used
9015
9016     getFileChunkBE(file, chunk_name, NULL);
9017     if (!strEqual(chunk_name, "TAPE"))
9018     {
9019       tape.no_valid_file = TRUE;
9020
9021       Warn("unknown format of tape file '%s'", filename);
9022
9023       closeFile(file);
9024
9025       return;
9026     }
9027   }
9028   else  // check for pre-2.0 file format with cookie string
9029   {
9030     strcpy(cookie, chunk_name);
9031     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9032       cookie[4] = '\0';
9033     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9034       cookie[strlen(cookie) - 1] = '\0';
9035
9036     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9037     {
9038       tape.no_valid_file = TRUE;
9039
9040       Warn("unknown format of tape file '%s'", filename);
9041
9042       closeFile(file);
9043
9044       return;
9045     }
9046
9047     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9048     {
9049       tape.no_valid_file = TRUE;
9050
9051       Warn("unsupported version of tape file '%s'", filename);
9052
9053       closeFile(file);
9054
9055       return;
9056     }
9057
9058     // pre-2.0 tape files have no game version, so use file version here
9059     tape.game_version = tape.file_version;
9060   }
9061
9062   if (tape.file_version < FILE_VERSION_1_2)
9063   {
9064     // tape files from versions before 1.2.0 without chunk structure
9065     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9066     LoadTape_BODY(file, 2 * tape.length,      &tape);
9067   }
9068   else
9069   {
9070     static struct
9071     {
9072       char *name;
9073       int size;
9074       int (*loader)(File *, int, struct TapeInfo *);
9075     }
9076     chunk_info[] =
9077     {
9078       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9079       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9080       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9081       { "INFO", -1,                     LoadTape_INFO },
9082       { "BODY", -1,                     LoadTape_BODY },
9083       {  NULL,  0,                      NULL }
9084     };
9085
9086     while (getFileChunkBE(file, chunk_name, &chunk_size))
9087     {
9088       int i = 0;
9089
9090       while (chunk_info[i].name != NULL &&
9091              !strEqual(chunk_name, chunk_info[i].name))
9092         i++;
9093
9094       if (chunk_info[i].name == NULL)
9095       {
9096         Warn("unknown chunk '%s' in tape file '%s'",
9097               chunk_name, filename);
9098
9099         ReadUnusedBytesFromFile(file, chunk_size);
9100       }
9101       else if (chunk_info[i].size != -1 &&
9102                chunk_info[i].size != chunk_size)
9103       {
9104         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9105               chunk_size, chunk_name, filename);
9106
9107         ReadUnusedBytesFromFile(file, chunk_size);
9108       }
9109       else
9110       {
9111         // call function to load this tape chunk
9112         int chunk_size_expected =
9113           (chunk_info[i].loader)(file, chunk_size, &tape);
9114
9115         // the size of some chunks cannot be checked before reading other
9116         // chunks first (like "HEAD" and "BODY") that contain some header
9117         // information, so check them here
9118         if (chunk_size_expected != chunk_size)
9119         {
9120           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9121                 chunk_size, chunk_name, filename);
9122         }
9123       }
9124     }
9125   }
9126
9127   closeFile(file);
9128
9129   tape.length_frames  = GetTapeLengthFrames();
9130   tape.length_seconds = GetTapeLengthSeconds();
9131
9132 #if 0
9133   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9134         tape.file_version);
9135   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9136         tape.game_version);
9137   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9138         tape.engine_version);
9139 #endif
9140 }
9141
9142 void LoadTape(int nr)
9143 {
9144   char *filename = getTapeFilename(nr);
9145
9146   LoadTapeFromFilename(filename);
9147 }
9148
9149 void LoadSolutionTape(int nr)
9150 {
9151   char *filename = getSolutionTapeFilename(nr);
9152
9153   LoadTapeFromFilename(filename);
9154
9155   if (TAPE_IS_EMPTY(tape))
9156   {
9157     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9158         level.native_bd_level->replay != NULL)
9159       CopyNativeTape_BD_to_RND(&level);
9160     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9161         level.native_sp_level->demo.is_available)
9162       CopyNativeTape_SP_to_RND(&level);
9163   }
9164 }
9165
9166 void LoadScoreTape(char *score_tape_basename, int nr)
9167 {
9168   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9169
9170   LoadTapeFromFilename(filename);
9171 }
9172
9173 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9174 {
9175   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9176
9177   LoadTapeFromFilename(filename);
9178 }
9179
9180 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9181 {
9182   // chunk required for team mode tapes with non-default screen size
9183   return (tape->num_participating_players > 1 &&
9184           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9185            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9186 }
9187
9188 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9189 {
9190   putFileVersion(file, tape->file_version);
9191   putFileVersion(file, tape->game_version);
9192 }
9193
9194 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9195 {
9196   int i;
9197   byte store_participating_players = 0;
9198
9199   // set bits for participating players for compact storage
9200   for (i = 0; i < MAX_PLAYERS; i++)
9201     if (tape->player_participates[i])
9202       store_participating_players |= (1 << i);
9203
9204   putFile32BitBE(file, tape->random_seed);
9205   putFile32BitBE(file, tape->date);
9206   putFile32BitBE(file, tape->length);
9207
9208   putFile8Bit(file, store_participating_players);
9209
9210   putFile8Bit(file, getTapeActionValue(tape));
9211
9212   putFile8Bit(file, tape->property_bits);
9213   putFile8Bit(file, tape->solved);
9214
9215   putFileVersion(file, tape->engine_version);
9216 }
9217
9218 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9219 {
9220   putFile8Bit(file, tape->scr_fieldx);
9221   putFile8Bit(file, tape->scr_fieldy);
9222 }
9223
9224 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9225 {
9226   int level_identifier_size = strlen(tape->level_identifier) + 1;
9227   int i;
9228
9229   putFile16BitBE(file, level_identifier_size);
9230
9231   for (i = 0; i < level_identifier_size; i++)
9232     putFile8Bit(file, tape->level_identifier[i]);
9233
9234   putFile16BitBE(file, tape->level_nr);
9235 }
9236
9237 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9238 {
9239   int i, j;
9240
9241   for (i = 0; i < tape->length; i++)
9242   {
9243     if (tape->use_key_actions)
9244     {
9245       for (j = 0; j < MAX_PLAYERS; j++)
9246         if (tape->player_participates[j])
9247           putFile8Bit(file, tape->pos[i].action[j]);
9248     }
9249
9250     if (tape->use_mouse_actions)
9251     {
9252       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9253       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9254       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9255     }
9256
9257     putFile8Bit(file, tape->pos[i].delay);
9258   }
9259 }
9260
9261 void SaveTapeToFilename(char *filename)
9262 {
9263   FILE *file;
9264   int tape_pos_size;
9265   int info_chunk_size;
9266   int body_chunk_size;
9267
9268   if (!(file = fopen(filename, MODE_WRITE)))
9269   {
9270     Warn("cannot save level recording file '%s'", filename);
9271
9272     return;
9273   }
9274
9275   tape_pos_size = getTapePosSize(&tape);
9276
9277   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9278   body_chunk_size = tape_pos_size * tape.length;
9279
9280   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9281   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9282
9283   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9284   SaveTape_VERS(file, &tape);
9285
9286   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9287   SaveTape_HEAD(file, &tape);
9288
9289   if (checkSaveTape_SCRN(&tape))
9290   {
9291     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9292     SaveTape_SCRN(file, &tape);
9293   }
9294
9295   putFileChunkBE(file, "INFO", info_chunk_size);
9296   SaveTape_INFO(file, &tape);
9297
9298   putFileChunkBE(file, "BODY", body_chunk_size);
9299   SaveTape_BODY(file, &tape);
9300
9301   fclose(file);
9302
9303   SetFilePermissions(filename, PERMS_PRIVATE);
9304 }
9305
9306 static void SaveTapeExt(char *filename)
9307 {
9308   int i;
9309
9310   tape.file_version = FILE_VERSION_ACTUAL;
9311   tape.game_version = GAME_VERSION_ACTUAL;
9312
9313   tape.num_participating_players = 0;
9314
9315   // count number of participating players
9316   for (i = 0; i < MAX_PLAYERS; i++)
9317     if (tape.player_participates[i])
9318       tape.num_participating_players++;
9319
9320   SaveTapeToFilename(filename);
9321
9322   tape.changed = FALSE;
9323 }
9324
9325 void SaveTape(int nr)
9326 {
9327   char *filename = getTapeFilename(nr);
9328
9329   InitTapeDirectory(leveldir_current->subdir);
9330
9331   SaveTapeExt(filename);
9332 }
9333
9334 void SaveScoreTape(int nr)
9335 {
9336   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9337
9338   // used instead of "leveldir_current->subdir" (for network games)
9339   InitScoreTapeDirectory(levelset.identifier, nr);
9340
9341   SaveTapeExt(filename);
9342 }
9343
9344 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9345                                   unsigned int req_state_added)
9346 {
9347   char *filename = getTapeFilename(nr);
9348   boolean new_tape = !fileExists(filename);
9349   boolean tape_saved = FALSE;
9350
9351   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9352   {
9353     SaveTape(nr);
9354
9355     if (new_tape)
9356       Request(msg_saved, REQ_CONFIRM | req_state_added);
9357
9358     tape_saved = TRUE;
9359   }
9360
9361   return tape_saved;
9362 }
9363
9364 boolean SaveTapeChecked(int nr)
9365 {
9366   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9367 }
9368
9369 boolean SaveTapeChecked_LevelSolved(int nr)
9370 {
9371   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9372                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9373 }
9374
9375 void DumpTape(struct TapeInfo *tape)
9376 {
9377   int tape_frame_counter;
9378   int i, j;
9379
9380   if (tape->no_valid_file)
9381   {
9382     Warn("cannot dump -- no valid tape file found");
9383
9384     return;
9385   }
9386
9387   PrintLine("-", 79);
9388
9389   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9390         tape->level_nr, tape->file_version, tape->game_version);
9391   Print("                  (effective engine version %08d)\n",
9392         tape->engine_version);
9393   Print("Level series identifier: '%s'\n", tape->level_identifier);
9394
9395   Print("Solution tape: %s\n",
9396         tape->solved ? "yes" :
9397         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9398
9399   Print("Special tape properties: ");
9400   if (tape->property_bits == TAPE_PROPERTY_NONE)
9401     Print("[none]");
9402   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9403     Print("[em_random_bug]");
9404   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9405     Print("[game_speed]");
9406   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9407     Print("[pause]");
9408   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9409     Print("[single_step]");
9410   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9411     Print("[snapshot]");
9412   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9413     Print("[replayed]");
9414   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9415     Print("[tas_keys]");
9416   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9417     Print("[small_graphics]");
9418   Print("\n");
9419
9420   int year2 = tape->date / 10000;
9421   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9422   int month_index_raw = (tape->date / 100) % 100;
9423   int month_index = month_index_raw % 12;       // prevent invalid index
9424   int month = month_index + 1;
9425   int day = tape->date % 100;
9426
9427   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9428
9429   PrintLine("-", 79);
9430
9431   tape_frame_counter = 0;
9432
9433   for (i = 0; i < tape->length; i++)
9434   {
9435     if (i >= MAX_TAPE_LEN)
9436       break;
9437
9438     Print("%04d: ", i);
9439
9440     for (j = 0; j < MAX_PLAYERS; j++)
9441     {
9442       if (tape->player_participates[j])
9443       {
9444         int action = tape->pos[i].action[j];
9445
9446         Print("%d:%02x ", j, action);
9447         Print("[%c%c%c%c|%c%c] - ",
9448               (action & JOY_LEFT ? '<' : ' '),
9449               (action & JOY_RIGHT ? '>' : ' '),
9450               (action & JOY_UP ? '^' : ' '),
9451               (action & JOY_DOWN ? 'v' : ' '),
9452               (action & JOY_BUTTON_1 ? '1' : ' '),
9453               (action & JOY_BUTTON_2 ? '2' : ' '));
9454       }
9455     }
9456
9457     Print("(%03d) ", tape->pos[i].delay);
9458     Print("[%05d]\n", tape_frame_counter);
9459
9460     tape_frame_counter += tape->pos[i].delay;
9461   }
9462
9463   PrintLine("-", 79);
9464 }
9465
9466 void DumpTapes(void)
9467 {
9468   static LevelDirTree *dumptape_leveldir = NULL;
9469
9470   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9471                                                 global.dumptape_leveldir);
9472
9473   if (dumptape_leveldir == NULL)
9474     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9475
9476   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9477       global.dumptape_level_nr > dumptape_leveldir->last_level)
9478     Fail("no such level number: %d", global.dumptape_level_nr);
9479
9480   leveldir_current = dumptape_leveldir;
9481
9482   if (options.mytapes)
9483     LoadTape(global.dumptape_level_nr);
9484   else
9485     LoadSolutionTape(global.dumptape_level_nr);
9486
9487   DumpTape(&tape);
9488
9489   CloseAllAndExit(0);
9490 }
9491
9492
9493 // ============================================================================
9494 // score file functions
9495 // ============================================================================
9496
9497 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9498 {
9499   int i;
9500
9501   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9502   {
9503     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9504     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9505     scores->entry[i].score = 0;
9506     scores->entry[i].time = 0;
9507
9508     scores->entry[i].id = -1;
9509     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9510     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9511     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9512     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9513     strcpy(scores->entry[i].country_code, "??");
9514   }
9515
9516   scores->num_entries = 0;
9517   scores->last_added = -1;
9518   scores->last_added_local = -1;
9519
9520   scores->updated = FALSE;
9521   scores->uploaded = FALSE;
9522   scores->tape_downloaded = FALSE;
9523   scores->force_last_added = FALSE;
9524
9525   // The following values are intentionally not reset here:
9526   // - last_level_nr
9527   // - last_entry_nr
9528   // - next_level_nr
9529   // - continue_playing
9530   // - continue_on_return
9531 }
9532
9533 static void setScoreInfoToDefaults(void)
9534 {
9535   setScoreInfoToDefaultsExt(&scores);
9536 }
9537
9538 static void setServerScoreInfoToDefaults(void)
9539 {
9540   setScoreInfoToDefaultsExt(&server_scores);
9541 }
9542
9543 static void LoadScore_OLD(int nr)
9544 {
9545   int i;
9546   char *filename = getScoreFilename(nr);
9547   char cookie[MAX_LINE_LEN];
9548   char line[MAX_LINE_LEN];
9549   char *line_ptr;
9550   FILE *file;
9551
9552   if (!(file = fopen(filename, MODE_READ)))
9553     return;
9554
9555   // check file identifier
9556   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9557     cookie[0] = '\0';
9558   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9559     cookie[strlen(cookie) - 1] = '\0';
9560
9561   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9562   {
9563     Warn("unknown format of score file '%s'", filename);
9564
9565     fclose(file);
9566
9567     return;
9568   }
9569
9570   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9571   {
9572     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9573       Warn("fscanf() failed; %s", strerror(errno));
9574
9575     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9576       line[0] = '\0';
9577
9578     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9579       line[strlen(line) - 1] = '\0';
9580
9581     for (line_ptr = line; *line_ptr; line_ptr++)
9582     {
9583       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9584       {
9585         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9586         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9587         break;
9588       }
9589     }
9590   }
9591
9592   fclose(file);
9593 }
9594
9595 static void ConvertScore_OLD(void)
9596 {
9597   // only convert score to time for levels that rate playing time over score
9598   if (!level.rate_time_over_score)
9599     return;
9600
9601   // convert old score to playing time for score-less levels (like Supaplex)
9602   int time_final_max = 999;
9603   int i;
9604
9605   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9606   {
9607     int score = scores.entry[i].score;
9608
9609     if (score > 0 && score < time_final_max)
9610       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9611   }
9612 }
9613
9614 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9615 {
9616   scores->file_version = getFileVersion(file);
9617   scores->game_version = getFileVersion(file);
9618
9619   return chunk_size;
9620 }
9621
9622 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9623 {
9624   char *level_identifier = NULL;
9625   int level_identifier_size;
9626   int i;
9627
9628   level_identifier_size = getFile16BitBE(file);
9629
9630   level_identifier = checked_malloc(level_identifier_size);
9631
9632   for (i = 0; i < level_identifier_size; i++)
9633     level_identifier[i] = getFile8Bit(file);
9634
9635   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9636   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9637
9638   checked_free(level_identifier);
9639
9640   scores->level_nr = getFile16BitBE(file);
9641   scores->num_entries = getFile16BitBE(file);
9642
9643   chunk_size = 2 + level_identifier_size + 2 + 2;
9644
9645   return chunk_size;
9646 }
9647
9648 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9649 {
9650   int i, j;
9651
9652   for (i = 0; i < scores->num_entries; i++)
9653   {
9654     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9655       scores->entry[i].name[j] = getFile8Bit(file);
9656
9657     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9658   }
9659
9660   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9661
9662   return chunk_size;
9663 }
9664
9665 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9666 {
9667   int i;
9668
9669   for (i = 0; i < scores->num_entries; i++)
9670     scores->entry[i].score = getFile16BitBE(file);
9671
9672   chunk_size = scores->num_entries * 2;
9673
9674   return chunk_size;
9675 }
9676
9677 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9678 {
9679   int i;
9680
9681   for (i = 0; i < scores->num_entries; i++)
9682     scores->entry[i].score = getFile32BitBE(file);
9683
9684   chunk_size = scores->num_entries * 4;
9685
9686   return chunk_size;
9687 }
9688
9689 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9690 {
9691   int i;
9692
9693   for (i = 0; i < scores->num_entries; i++)
9694     scores->entry[i].time = getFile32BitBE(file);
9695
9696   chunk_size = scores->num_entries * 4;
9697
9698   return chunk_size;
9699 }
9700
9701 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9702 {
9703   int i, j;
9704
9705   for (i = 0; i < scores->num_entries; i++)
9706   {
9707     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9708       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9709
9710     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9711   }
9712
9713   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9714
9715   return chunk_size;
9716 }
9717
9718 void LoadScore(int nr)
9719 {
9720   char *filename = getScoreFilename(nr);
9721   char cookie[MAX_LINE_LEN];
9722   char chunk_name[CHUNK_ID_LEN + 1];
9723   int chunk_size;
9724   boolean old_score_file_format = FALSE;
9725   File *file;
9726
9727   // always start with reliable default values
9728   setScoreInfoToDefaults();
9729
9730   if (!(file = openFile(filename, MODE_READ)))
9731     return;
9732
9733   getFileChunkBE(file, chunk_name, NULL);
9734   if (strEqual(chunk_name, "RND1"))
9735   {
9736     getFile32BitBE(file);               // not used
9737
9738     getFileChunkBE(file, chunk_name, NULL);
9739     if (!strEqual(chunk_name, "SCOR"))
9740     {
9741       Warn("unknown format of score file '%s'", filename);
9742
9743       closeFile(file);
9744
9745       return;
9746     }
9747   }
9748   else  // check for old file format with cookie string
9749   {
9750     strcpy(cookie, chunk_name);
9751     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9752       cookie[4] = '\0';
9753     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9754       cookie[strlen(cookie) - 1] = '\0';
9755
9756     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9757     {
9758       Warn("unknown format of score file '%s'", filename);
9759
9760       closeFile(file);
9761
9762       return;
9763     }
9764
9765     old_score_file_format = TRUE;
9766   }
9767
9768   if (old_score_file_format)
9769   {
9770     // score files from versions before 4.2.4.0 without chunk structure
9771     LoadScore_OLD(nr);
9772
9773     // convert score to time, if possible (mainly for Supaplex levels)
9774     ConvertScore_OLD();
9775   }
9776   else
9777   {
9778     static struct
9779     {
9780       char *name;
9781       int size;
9782       int (*loader)(File *, int, struct ScoreInfo *);
9783     }
9784     chunk_info[] =
9785     {
9786       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9787       { "INFO", -1,                     LoadScore_INFO },
9788       { "NAME", -1,                     LoadScore_NAME },
9789       { "SCOR", -1,                     LoadScore_SCOR },
9790       { "SC4R", -1,                     LoadScore_SC4R },
9791       { "TIME", -1,                     LoadScore_TIME },
9792       { "TAPE", -1,                     LoadScore_TAPE },
9793
9794       {  NULL,  0,                      NULL }
9795     };
9796
9797     while (getFileChunkBE(file, chunk_name, &chunk_size))
9798     {
9799       int i = 0;
9800
9801       while (chunk_info[i].name != NULL &&
9802              !strEqual(chunk_name, chunk_info[i].name))
9803         i++;
9804
9805       if (chunk_info[i].name == NULL)
9806       {
9807         Warn("unknown chunk '%s' in score file '%s'",
9808               chunk_name, filename);
9809
9810         ReadUnusedBytesFromFile(file, chunk_size);
9811       }
9812       else if (chunk_info[i].size != -1 &&
9813                chunk_info[i].size != chunk_size)
9814       {
9815         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9816               chunk_size, chunk_name, filename);
9817
9818         ReadUnusedBytesFromFile(file, chunk_size);
9819       }
9820       else
9821       {
9822         // call function to load this score chunk
9823         int chunk_size_expected =
9824           (chunk_info[i].loader)(file, chunk_size, &scores);
9825
9826         // the size of some chunks cannot be checked before reading other
9827         // chunks first (like "HEAD" and "BODY") that contain some header
9828         // information, so check them here
9829         if (chunk_size_expected != chunk_size)
9830         {
9831           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9832                 chunk_size, chunk_name, filename);
9833         }
9834       }
9835     }
9836   }
9837
9838   closeFile(file);
9839 }
9840
9841 #if ENABLE_HISTORIC_CHUNKS
9842 void SaveScore_OLD(int nr)
9843 {
9844   int i;
9845   char *filename = getScoreFilename(nr);
9846   FILE *file;
9847
9848   // used instead of "leveldir_current->subdir" (for network games)
9849   InitScoreDirectory(levelset.identifier);
9850
9851   if (!(file = fopen(filename, MODE_WRITE)))
9852   {
9853     Warn("cannot save score for level %d", nr);
9854
9855     return;
9856   }
9857
9858   fprintf(file, "%s\n\n", SCORE_COOKIE);
9859
9860   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9861     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9862
9863   fclose(file);
9864
9865   SetFilePermissions(filename, PERMS_PRIVATE);
9866 }
9867 #endif
9868
9869 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9870 {
9871   putFileVersion(file, scores->file_version);
9872   putFileVersion(file, scores->game_version);
9873 }
9874
9875 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9876 {
9877   int level_identifier_size = strlen(scores->level_identifier) + 1;
9878   int i;
9879
9880   putFile16BitBE(file, level_identifier_size);
9881
9882   for (i = 0; i < level_identifier_size; i++)
9883     putFile8Bit(file, scores->level_identifier[i]);
9884
9885   putFile16BitBE(file, scores->level_nr);
9886   putFile16BitBE(file, scores->num_entries);
9887 }
9888
9889 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9890 {
9891   int i, j;
9892
9893   for (i = 0; i < scores->num_entries; i++)
9894   {
9895     int name_size = strlen(scores->entry[i].name);
9896
9897     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9898       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9899   }
9900 }
9901
9902 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9903 {
9904   int i;
9905
9906   for (i = 0; i < scores->num_entries; i++)
9907     putFile16BitBE(file, scores->entry[i].score);
9908 }
9909
9910 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9911 {
9912   int i;
9913
9914   for (i = 0; i < scores->num_entries; i++)
9915     putFile32BitBE(file, scores->entry[i].score);
9916 }
9917
9918 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9919 {
9920   int i;
9921
9922   for (i = 0; i < scores->num_entries; i++)
9923     putFile32BitBE(file, scores->entry[i].time);
9924 }
9925
9926 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9927 {
9928   int i, j;
9929
9930   for (i = 0; i < scores->num_entries; i++)
9931   {
9932     int size = strlen(scores->entry[i].tape_basename);
9933
9934     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9935       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9936   }
9937 }
9938
9939 static void SaveScoreToFilename(char *filename)
9940 {
9941   FILE *file;
9942   int info_chunk_size;
9943   int name_chunk_size;
9944   int scor_chunk_size;
9945   int sc4r_chunk_size;
9946   int time_chunk_size;
9947   int tape_chunk_size;
9948   boolean has_large_score_values;
9949   int i;
9950
9951   if (!(file = fopen(filename, MODE_WRITE)))
9952   {
9953     Warn("cannot save score file '%s'", filename);
9954
9955     return;
9956   }
9957
9958   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9959   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9960   scor_chunk_size = scores.num_entries * 2;
9961   sc4r_chunk_size = scores.num_entries * 4;
9962   time_chunk_size = scores.num_entries * 4;
9963   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9964
9965   has_large_score_values = FALSE;
9966   for (i = 0; i < scores.num_entries; i++)
9967     if (scores.entry[i].score > 0xffff)
9968       has_large_score_values = TRUE;
9969
9970   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9971   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9972
9973   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9974   SaveScore_VERS(file, &scores);
9975
9976   putFileChunkBE(file, "INFO", info_chunk_size);
9977   SaveScore_INFO(file, &scores);
9978
9979   putFileChunkBE(file, "NAME", name_chunk_size);
9980   SaveScore_NAME(file, &scores);
9981
9982   if (has_large_score_values)
9983   {
9984     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9985     SaveScore_SC4R(file, &scores);
9986   }
9987   else
9988   {
9989     putFileChunkBE(file, "SCOR", scor_chunk_size);
9990     SaveScore_SCOR(file, &scores);
9991   }
9992
9993   putFileChunkBE(file, "TIME", time_chunk_size);
9994   SaveScore_TIME(file, &scores);
9995
9996   putFileChunkBE(file, "TAPE", tape_chunk_size);
9997   SaveScore_TAPE(file, &scores);
9998
9999   fclose(file);
10000
10001   SetFilePermissions(filename, PERMS_PRIVATE);
10002 }
10003
10004 void SaveScore(int nr)
10005 {
10006   char *filename = getScoreFilename(nr);
10007   int i;
10008
10009   // used instead of "leveldir_current->subdir" (for network games)
10010   InitScoreDirectory(levelset.identifier);
10011
10012   scores.file_version = FILE_VERSION_ACTUAL;
10013   scores.game_version = GAME_VERSION_ACTUAL;
10014
10015   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10016   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10017   scores.level_nr = level_nr;
10018
10019   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10020     if (scores.entry[i].score == 0 &&
10021         scores.entry[i].time == 0 &&
10022         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10023       break;
10024
10025   scores.num_entries = i;
10026
10027   if (scores.num_entries == 0)
10028     return;
10029
10030   SaveScoreToFilename(filename);
10031 }
10032
10033 static void LoadServerScoreFromCache(int nr)
10034 {
10035   struct ScoreEntry score_entry;
10036   struct
10037   {
10038     void *value;
10039     boolean is_string;
10040     int string_size;
10041   }
10042   score_mapping[] =
10043   {
10044     { &score_entry.score,               FALSE,  0                       },
10045     { &score_entry.time,                FALSE,  0                       },
10046     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10047     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10048     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10049     { &score_entry.id,                  FALSE,  0                       },
10050     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10051     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10052     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10053     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10054
10055     { NULL,                             FALSE,  0                       }
10056   };
10057   char *filename = getScoreCacheFilename(nr);
10058   SetupFileHash *score_hash = loadSetupFileHash(filename);
10059   int i, j;
10060
10061   server_scores.num_entries = 0;
10062
10063   if (score_hash == NULL)
10064     return;
10065
10066   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10067   {
10068     score_entry = server_scores.entry[i];
10069
10070     for (j = 0; score_mapping[j].value != NULL; j++)
10071     {
10072       char token[10];
10073
10074       sprintf(token, "%02d.%d", i, j);
10075
10076       char *value = getHashEntry(score_hash, token);
10077
10078       if (value == NULL)
10079         continue;
10080
10081       if (score_mapping[j].is_string)
10082       {
10083         char *score_value = (char *)score_mapping[j].value;
10084         int value_size = score_mapping[j].string_size;
10085
10086         strncpy(score_value, value, value_size);
10087         score_value[value_size] = '\0';
10088       }
10089       else
10090       {
10091         int *score_value = (int *)score_mapping[j].value;
10092
10093         *score_value = atoi(value);
10094       }
10095
10096       server_scores.num_entries = i + 1;
10097     }
10098
10099     server_scores.entry[i] = score_entry;
10100   }
10101
10102   freeSetupFileHash(score_hash);
10103 }
10104
10105 void LoadServerScore(int nr, boolean download_score)
10106 {
10107   if (!setup.use_api_server)
10108     return;
10109
10110   // always start with reliable default values
10111   setServerScoreInfoToDefaults();
10112
10113   // 1st step: load server scores from cache file (which may not exist)
10114   // (this should prevent reading it while the thread is writing to it)
10115   LoadServerScoreFromCache(nr);
10116
10117   if (download_score && runtime.use_api_server)
10118   {
10119     // 2nd step: download server scores from score server to cache file
10120     // (as thread, as it might time out if the server is not reachable)
10121     ApiGetScoreAsThread(nr);
10122   }
10123 }
10124
10125 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10126 {
10127   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10128
10129   // if score tape not uploaded, ask for uploading missing tapes later
10130   if (!setup.has_remaining_tapes)
10131     setup.ask_for_remaining_tapes = TRUE;
10132
10133   setup.provide_uploading_tapes = TRUE;
10134   setup.has_remaining_tapes = TRUE;
10135
10136   SaveSetup_ServerSetup();
10137 }
10138
10139 void SaveServerScore(int nr, boolean tape_saved)
10140 {
10141   if (!runtime.use_api_server)
10142   {
10143     PrepareScoreTapesForUpload(leveldir_current->subdir);
10144
10145     return;
10146   }
10147
10148   ApiAddScoreAsThread(nr, tape_saved, NULL);
10149 }
10150
10151 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10152                              char *score_tape_filename)
10153 {
10154   if (!runtime.use_api_server)
10155     return;
10156
10157   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10158 }
10159
10160 void LoadLocalAndServerScore(int nr, boolean download_score)
10161 {
10162   int last_added_local = scores.last_added_local;
10163   boolean force_last_added = scores.force_last_added;
10164
10165   // needed if only showing server scores
10166   setScoreInfoToDefaults();
10167
10168   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10169     LoadScore(nr);
10170
10171   // restore last added local score entry (before merging server scores)
10172   scores.last_added = scores.last_added_local = last_added_local;
10173
10174   if (setup.use_api_server &&
10175       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10176   {
10177     // load server scores from cache file and trigger update from server
10178     LoadServerScore(nr, download_score);
10179
10180     // merge local scores with scores from server
10181     MergeServerScore();
10182   }
10183
10184   if (force_last_added)
10185     scores.force_last_added = force_last_added;
10186 }
10187
10188
10189 // ============================================================================
10190 // setup file functions
10191 // ============================================================================
10192
10193 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10194
10195
10196 static struct TokenInfo global_setup_tokens[] =
10197 {
10198   {
10199     TYPE_STRING,
10200     &setup.player_name,                         "player_name"
10201   },
10202   {
10203     TYPE_SWITCH,
10204     &setup.multiple_users,                      "multiple_users"
10205   },
10206   {
10207     TYPE_SWITCH,
10208     &setup.sound,                               "sound"
10209   },
10210   {
10211     TYPE_SWITCH,
10212     &setup.sound_loops,                         "repeating_sound_loops"
10213   },
10214   {
10215     TYPE_SWITCH,
10216     &setup.sound_music,                         "background_music"
10217   },
10218   {
10219     TYPE_SWITCH,
10220     &setup.sound_simple,                        "simple_sound_effects"
10221   },
10222   {
10223     TYPE_SWITCH,
10224     &setup.toons,                               "toons"
10225   },
10226   {
10227     TYPE_SWITCH,
10228     &setup.global_animations,                   "global_animations"
10229   },
10230   {
10231     TYPE_SWITCH,
10232     &setup.scroll_delay,                        "scroll_delay"
10233   },
10234   {
10235     TYPE_SWITCH,
10236     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10237   },
10238   {
10239     TYPE_INTEGER,
10240     &setup.scroll_delay_value,                  "scroll_delay_value"
10241   },
10242   {
10243     TYPE_STRING,
10244     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10245   },
10246   {
10247     TYPE_INTEGER,
10248     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10249   },
10250   {
10251     TYPE_SWITCH,
10252     &setup.fade_screens,                        "fade_screens"
10253   },
10254   {
10255     TYPE_SWITCH,
10256     &setup.autorecord,                          "automatic_tape_recording"
10257   },
10258   {
10259     TYPE_SWITCH,
10260     &setup.autorecord_after_replay,             "autorecord_after_replay"
10261   },
10262   {
10263     TYPE_SWITCH,
10264     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10265   },
10266   {
10267     TYPE_SWITCH,
10268     &setup.show_titlescreen,                    "show_titlescreen"
10269   },
10270   {
10271     TYPE_SWITCH,
10272     &setup.quick_doors,                         "quick_doors"
10273   },
10274   {
10275     TYPE_SWITCH,
10276     &setup.team_mode,                           "team_mode"
10277   },
10278   {
10279     TYPE_SWITCH,
10280     &setup.handicap,                            "handicap"
10281   },
10282   {
10283     TYPE_SWITCH,
10284     &setup.skip_levels,                         "skip_levels"
10285   },
10286   {
10287     TYPE_SWITCH,
10288     &setup.increment_levels,                    "increment_levels"
10289   },
10290   {
10291     TYPE_SWITCH,
10292     &setup.auto_play_next_level,                "auto_play_next_level"
10293   },
10294   {
10295     TYPE_SWITCH,
10296     &setup.count_score_after_game,              "count_score_after_game"
10297   },
10298   {
10299     TYPE_SWITCH,
10300     &setup.show_scores_after_game,              "show_scores_after_game"
10301   },
10302   {
10303     TYPE_SWITCH,
10304     &setup.time_limit,                          "time_limit"
10305   },
10306   {
10307     TYPE_SWITCH,
10308     &setup.fullscreen,                          "fullscreen"
10309   },
10310   {
10311     TYPE_INTEGER,
10312     &setup.window_scaling_percent,              "window_scaling_percent"
10313   },
10314   {
10315     TYPE_STRING,
10316     &setup.window_scaling_quality,              "window_scaling_quality"
10317   },
10318   {
10319     TYPE_STRING,
10320     &setup.screen_rendering_mode,               "screen_rendering_mode"
10321   },
10322   {
10323     TYPE_STRING,
10324     &setup.vsync_mode,                          "vsync_mode"
10325   },
10326   {
10327     TYPE_SWITCH,
10328     &setup.ask_on_escape,                       "ask_on_escape"
10329   },
10330   {
10331     TYPE_SWITCH,
10332     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10333   },
10334   {
10335     TYPE_SWITCH,
10336     &setup.ask_on_game_over,                    "ask_on_game_over"
10337   },
10338   {
10339     TYPE_SWITCH,
10340     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10341   },
10342   {
10343     TYPE_SWITCH,
10344     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10345   },
10346   {
10347     TYPE_SWITCH,
10348     &setup.quick_switch,                        "quick_player_switch"
10349   },
10350   {
10351     TYPE_SWITCH,
10352     &setup.input_on_focus,                      "input_on_focus"
10353   },
10354   {
10355     TYPE_SWITCH,
10356     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10357   },
10358   {
10359     TYPE_SWITCH,
10360     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10361   },
10362   {
10363     TYPE_SWITCH,
10364     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10365   },
10366   {
10367     TYPE_SWITCH,
10368     &setup.game_speed_extended,                 "game_speed_extended"
10369   },
10370   {
10371     TYPE_INTEGER,
10372     &setup.game_frame_delay,                    "game_frame_delay"
10373   },
10374   {
10375     TYPE_SWITCH,
10376     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10377   },
10378   {
10379     TYPE_SWITCH,
10380     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10381   },
10382   {
10383     TYPE_SWITCH,
10384     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10385   },
10386   {
10387     TYPE_SWITCH3,
10388     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10389   },
10390   {
10391     TYPE_SWITCH,
10392     &setup.sp_show_border_elements,             "sp_show_border_elements"
10393   },
10394   {
10395     TYPE_SWITCH,
10396     &setup.small_game_graphics,                 "small_game_graphics"
10397   },
10398   {
10399     TYPE_SWITCH,
10400     &setup.show_load_save_buttons,              "show_load_save_buttons"
10401   },
10402   {
10403     TYPE_SWITCH,
10404     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10405   },
10406   {
10407     TYPE_STRING,
10408     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10409   },
10410   {
10411     TYPE_STRING,
10412     &setup.graphics_set,                        "graphics_set"
10413   },
10414   {
10415     TYPE_STRING,
10416     &setup.sounds_set,                          "sounds_set"
10417   },
10418   {
10419     TYPE_STRING,
10420     &setup.music_set,                           "music_set"
10421   },
10422   {
10423     TYPE_SWITCH3,
10424     &setup.override_level_graphics,             "override_level_graphics"
10425   },
10426   {
10427     TYPE_SWITCH3,
10428     &setup.override_level_sounds,               "override_level_sounds"
10429   },
10430   {
10431     TYPE_SWITCH3,
10432     &setup.override_level_music,                "override_level_music"
10433   },
10434   {
10435     TYPE_INTEGER,
10436     &setup.volume_simple,                       "volume_simple"
10437   },
10438   {
10439     TYPE_INTEGER,
10440     &setup.volume_loops,                        "volume_loops"
10441   },
10442   {
10443     TYPE_INTEGER,
10444     &setup.volume_music,                        "volume_music"
10445   },
10446   {
10447     TYPE_SWITCH,
10448     &setup.network_mode,                        "network_mode"
10449   },
10450   {
10451     TYPE_PLAYER,
10452     &setup.network_player_nr,                   "network_player"
10453   },
10454   {
10455     TYPE_STRING,
10456     &setup.network_server_hostname,             "network_server_hostname"
10457   },
10458   {
10459     TYPE_STRING,
10460     &setup.touch.control_type,                  "touch.control_type"
10461   },
10462   {
10463     TYPE_INTEGER,
10464     &setup.touch.move_distance,                 "touch.move_distance"
10465   },
10466   {
10467     TYPE_INTEGER,
10468     &setup.touch.drop_distance,                 "touch.drop_distance"
10469   },
10470   {
10471     TYPE_INTEGER,
10472     &setup.touch.transparency,                  "touch.transparency"
10473   },
10474   {
10475     TYPE_INTEGER,
10476     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10477   },
10478   {
10479     TYPE_INTEGER,
10480     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10481   },
10482   {
10483     TYPE_INTEGER,
10484     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10485   },
10486   {
10487     TYPE_INTEGER,
10488     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10489   },
10490   {
10491     TYPE_INTEGER,
10492     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10493   },
10494   {
10495     TYPE_INTEGER,
10496     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10497   },
10498   {
10499     TYPE_SWITCH,
10500     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10501   },
10502 };
10503
10504 static struct TokenInfo auto_setup_tokens[] =
10505 {
10506   {
10507     TYPE_INTEGER,
10508     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10509   },
10510 };
10511
10512 static struct TokenInfo server_setup_tokens[] =
10513 {
10514   {
10515     TYPE_STRING,
10516     &setup.player_uuid,                         "player_uuid"
10517   },
10518   {
10519     TYPE_INTEGER,
10520     &setup.player_version,                      "player_version"
10521   },
10522   {
10523     TYPE_SWITCH,
10524     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10525   },
10526   {
10527     TYPE_STRING,
10528     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10529   },
10530   {
10531     TYPE_STRING,
10532     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10533   },
10534   {
10535     TYPE_SWITCH,
10536     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10537   },
10538   {
10539     TYPE_SWITCH,
10540     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10541   },
10542   {
10543     TYPE_SWITCH,
10544     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10545   },
10546   {
10547     TYPE_SWITCH,
10548     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10549   },
10550   {
10551     TYPE_SWITCH,
10552     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10553   },
10554 };
10555
10556 static struct TokenInfo editor_setup_tokens[] =
10557 {
10558   {
10559     TYPE_SWITCH,
10560     &setup.editor.el_classic,                   "editor.el_classic"
10561   },
10562   {
10563     TYPE_SWITCH,
10564     &setup.editor.el_custom,                    "editor.el_custom"
10565   },
10566   {
10567     TYPE_SWITCH,
10568     &setup.editor.el_user_defined,              "editor.el_user_defined"
10569   },
10570   {
10571     TYPE_SWITCH,
10572     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10573   },
10574   {
10575     TYPE_SWITCH,
10576     &setup.editor.el_headlines,                 "editor.el_headlines"
10577   },
10578   {
10579     TYPE_SWITCH,
10580     &setup.editor.show_element_token,           "editor.show_element_token"
10581   },
10582   {
10583     TYPE_SWITCH,
10584     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10585   },
10586 };
10587
10588 static struct TokenInfo editor_cascade_setup_tokens[] =
10589 {
10590   {
10591     TYPE_SWITCH,
10592     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10593   },
10594   {
10595     TYPE_SWITCH,
10596     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10597   },
10598   {
10599     TYPE_SWITCH,
10600     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10601   },
10602   {
10603     TYPE_SWITCH,
10604     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10605   },
10606   {
10607     TYPE_SWITCH,
10608     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10609   },
10610   {
10611     TYPE_SWITCH,
10612     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10617   },
10618   {
10619     TYPE_SWITCH,
10620     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10621   },
10622   {
10623     TYPE_SWITCH,
10624     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10625   },
10626   {
10627     TYPE_SWITCH,
10628     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10629   },
10630   {
10631     TYPE_SWITCH,
10632     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10645   },
10646   {
10647     TYPE_SWITCH,
10648     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10649   },
10650   {
10651     TYPE_SWITCH,
10652     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10653   },
10654   {
10655     TYPE_SWITCH,
10656     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10657   },
10658   {
10659     TYPE_SWITCH,
10660     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10661   },
10662   {
10663     TYPE_SWITCH,
10664     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10665   },
10666   {
10667     TYPE_SWITCH,
10668     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10669   },
10670 };
10671
10672 static struct TokenInfo shortcut_setup_tokens[] =
10673 {
10674   {
10675     TYPE_KEY_X11,
10676     &setup.shortcut.save_game,                  "shortcut.save_game"
10677   },
10678   {
10679     TYPE_KEY_X11,
10680     &setup.shortcut.load_game,                  "shortcut.load_game"
10681   },
10682   {
10683     TYPE_KEY_X11,
10684     &setup.shortcut.restart_game,               "shortcut.restart_game"
10685   },
10686   {
10687     TYPE_KEY_X11,
10688     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10689   },
10690   {
10691     TYPE_KEY_X11,
10692     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10693   },
10694   {
10695     TYPE_KEY_X11,
10696     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10697   },
10698   {
10699     TYPE_KEY_X11,
10700     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10701   },
10702   {
10703     TYPE_KEY_X11,
10704     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10705   },
10706   {
10707     TYPE_KEY_X11,
10708     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10709   },
10710   {
10711     TYPE_KEY_X11,
10712     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10713   },
10714   {
10715     TYPE_KEY_X11,
10716     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10717   },
10718   {
10719     TYPE_KEY_X11,
10720     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10721   },
10722   {
10723     TYPE_KEY_X11,
10724     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10725   },
10726   {
10727     TYPE_KEY_X11,
10728     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10729   },
10730   {
10731     TYPE_KEY_X11,
10732     &setup.shortcut.tape_record,                "shortcut.tape_record"
10733   },
10734   {
10735     TYPE_KEY_X11,
10736     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10737   },
10738   {
10739     TYPE_KEY_X11,
10740     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10741   },
10742   {
10743     TYPE_KEY_X11,
10744     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10745   },
10746   {
10747     TYPE_KEY_X11,
10748     &setup.shortcut.sound_music,                "shortcut.sound_music"
10749   },
10750   {
10751     TYPE_KEY_X11,
10752     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10753   },
10754   {
10755     TYPE_KEY_X11,
10756     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10757   },
10758   {
10759     TYPE_KEY_X11,
10760     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10761   },
10762   {
10763     TYPE_KEY_X11,
10764     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10765   },
10766 };
10767
10768 static struct SetupInputInfo setup_input;
10769 static struct TokenInfo player_setup_tokens[] =
10770 {
10771   {
10772     TYPE_BOOLEAN,
10773     &setup_input.use_joystick,                  ".use_joystick"
10774   },
10775   {
10776     TYPE_STRING,
10777     &setup_input.joy.device_name,               ".joy.device_name"
10778   },
10779   {
10780     TYPE_INTEGER,
10781     &setup_input.joy.xleft,                     ".joy.xleft"
10782   },
10783   {
10784     TYPE_INTEGER,
10785     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10786   },
10787   {
10788     TYPE_INTEGER,
10789     &setup_input.joy.xright,                    ".joy.xright"
10790   },
10791   {
10792     TYPE_INTEGER,
10793     &setup_input.joy.yupper,                    ".joy.yupper"
10794   },
10795   {
10796     TYPE_INTEGER,
10797     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10798   },
10799   {
10800     TYPE_INTEGER,
10801     &setup_input.joy.ylower,                    ".joy.ylower"
10802   },
10803   {
10804     TYPE_INTEGER,
10805     &setup_input.joy.snap,                      ".joy.snap_field"
10806   },
10807   {
10808     TYPE_INTEGER,
10809     &setup_input.joy.drop,                      ".joy.place_bomb"
10810   },
10811   {
10812     TYPE_KEY_X11,
10813     &setup_input.key.left,                      ".key.move_left"
10814   },
10815   {
10816     TYPE_KEY_X11,
10817     &setup_input.key.right,                     ".key.move_right"
10818   },
10819   {
10820     TYPE_KEY_X11,
10821     &setup_input.key.up,                        ".key.move_up"
10822   },
10823   {
10824     TYPE_KEY_X11,
10825     &setup_input.key.down,                      ".key.move_down"
10826   },
10827   {
10828     TYPE_KEY_X11,
10829     &setup_input.key.snap,                      ".key.snap_field"
10830   },
10831   {
10832     TYPE_KEY_X11,
10833     &setup_input.key.drop,                      ".key.place_bomb"
10834   },
10835 };
10836
10837 static struct TokenInfo system_setup_tokens[] =
10838 {
10839   {
10840     TYPE_STRING,
10841     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10842   },
10843   {
10844     TYPE_STRING,
10845     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10846   },
10847   {
10848     TYPE_STRING,
10849     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10850   },
10851   {
10852     TYPE_INTEGER,
10853     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10854   },
10855 };
10856
10857 static struct TokenInfo internal_setup_tokens[] =
10858 {
10859   {
10860     TYPE_STRING,
10861     &setup.internal.program_title,              "program_title"
10862   },
10863   {
10864     TYPE_STRING,
10865     &setup.internal.program_version,            "program_version"
10866   },
10867   {
10868     TYPE_STRING,
10869     &setup.internal.program_author,             "program_author"
10870   },
10871   {
10872     TYPE_STRING,
10873     &setup.internal.program_email,              "program_email"
10874   },
10875   {
10876     TYPE_STRING,
10877     &setup.internal.program_website,            "program_website"
10878   },
10879   {
10880     TYPE_STRING,
10881     &setup.internal.program_copyright,          "program_copyright"
10882   },
10883   {
10884     TYPE_STRING,
10885     &setup.internal.program_company,            "program_company"
10886   },
10887   {
10888     TYPE_STRING,
10889     &setup.internal.program_icon_file,          "program_icon_file"
10890   },
10891   {
10892     TYPE_STRING,
10893     &setup.internal.default_graphics_set,       "default_graphics_set"
10894   },
10895   {
10896     TYPE_STRING,
10897     &setup.internal.default_sounds_set,         "default_sounds_set"
10898   },
10899   {
10900     TYPE_STRING,
10901     &setup.internal.default_music_set,          "default_music_set"
10902   },
10903   {
10904     TYPE_STRING,
10905     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10906   },
10907   {
10908     TYPE_STRING,
10909     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10910   },
10911   {
10912     TYPE_STRING,
10913     &setup.internal.fallback_music_file,        "fallback_music_file"
10914   },
10915   {
10916     TYPE_STRING,
10917     &setup.internal.default_level_series,       "default_level_series"
10918   },
10919   {
10920     TYPE_INTEGER,
10921     &setup.internal.default_window_width,       "default_window_width"
10922   },
10923   {
10924     TYPE_INTEGER,
10925     &setup.internal.default_window_height,      "default_window_height"
10926   },
10927   {
10928     TYPE_BOOLEAN,
10929     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10930   },
10931   {
10932     TYPE_BOOLEAN,
10933     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
10934   },
10935   {
10936     TYPE_BOOLEAN,
10937     &setup.internal.create_user_levelset,       "create_user_levelset"
10938   },
10939   {
10940     TYPE_BOOLEAN,
10941     &setup.internal.info_screens_from_main,     "info_screens_from_main"
10942   },
10943   {
10944     TYPE_BOOLEAN,
10945     &setup.internal.menu_game,                  "menu_game"
10946   },
10947   {
10948     TYPE_BOOLEAN,
10949     &setup.internal.menu_engines,               "menu_engines"
10950   },
10951   {
10952     TYPE_BOOLEAN,
10953     &setup.internal.menu_editor,                "menu_editor"
10954   },
10955   {
10956     TYPE_BOOLEAN,
10957     &setup.internal.menu_graphics,              "menu_graphics"
10958   },
10959   {
10960     TYPE_BOOLEAN,
10961     &setup.internal.menu_sound,                 "menu_sound"
10962   },
10963   {
10964     TYPE_BOOLEAN,
10965     &setup.internal.menu_artwork,               "menu_artwork"
10966   },
10967   {
10968     TYPE_BOOLEAN,
10969     &setup.internal.menu_input,                 "menu_input"
10970   },
10971   {
10972     TYPE_BOOLEAN,
10973     &setup.internal.menu_touch,                 "menu_touch"
10974   },
10975   {
10976     TYPE_BOOLEAN,
10977     &setup.internal.menu_shortcuts,             "menu_shortcuts"
10978   },
10979   {
10980     TYPE_BOOLEAN,
10981     &setup.internal.menu_exit,                  "menu_exit"
10982   },
10983   {
10984     TYPE_BOOLEAN,
10985     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
10986   },
10987   {
10988     TYPE_BOOLEAN,
10989     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
10990   },
10991   {
10992     TYPE_BOOLEAN,
10993     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
10994   },
10995   {
10996     TYPE_BOOLEAN,
10997     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
10998   },
10999   {
11000     TYPE_BOOLEAN,
11001     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11002   },
11003   {
11004     TYPE_BOOLEAN,
11005     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11006   },
11007   {
11008     TYPE_BOOLEAN,
11009     &setup.internal.info_title,                 "info_title"
11010   },
11011   {
11012     TYPE_BOOLEAN,
11013     &setup.internal.info_elements,              "info_elements"
11014   },
11015   {
11016     TYPE_BOOLEAN,
11017     &setup.internal.info_music,                 "info_music"
11018   },
11019   {
11020     TYPE_BOOLEAN,
11021     &setup.internal.info_credits,               "info_credits"
11022   },
11023   {
11024     TYPE_BOOLEAN,
11025     &setup.internal.info_program,               "info_program"
11026   },
11027   {
11028     TYPE_BOOLEAN,
11029     &setup.internal.info_version,               "info_version"
11030   },
11031   {
11032     TYPE_BOOLEAN,
11033     &setup.internal.info_levelset,              "info_levelset"
11034   },
11035   {
11036     TYPE_BOOLEAN,
11037     &setup.internal.info_exit,                  "info_exit"
11038   },
11039 };
11040
11041 static struct TokenInfo debug_setup_tokens[] =
11042 {
11043   {
11044     TYPE_INTEGER,
11045     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11046   },
11047   {
11048     TYPE_INTEGER,
11049     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11050   },
11051   {
11052     TYPE_INTEGER,
11053     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11054   },
11055   {
11056     TYPE_INTEGER,
11057     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11058   },
11059   {
11060     TYPE_INTEGER,
11061     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11062   },
11063   {
11064     TYPE_INTEGER,
11065     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11066   },
11067   {
11068     TYPE_INTEGER,
11069     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11070   },
11071   {
11072     TYPE_INTEGER,
11073     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11074   },
11075   {
11076     TYPE_INTEGER,
11077     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11078   },
11079   {
11080     TYPE_INTEGER,
11081     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11082   },
11083   {
11084     TYPE_KEY_X11,
11085     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11086   },
11087   {
11088     TYPE_KEY_X11,
11089     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11090   },
11091   {
11092     TYPE_KEY_X11,
11093     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11094   },
11095   {
11096     TYPE_KEY_X11,
11097     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11098   },
11099   {
11100     TYPE_KEY_X11,
11101     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11102   },
11103   {
11104     TYPE_KEY_X11,
11105     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11106   },
11107   {
11108     TYPE_KEY_X11,
11109     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11110   },
11111   {
11112     TYPE_KEY_X11,
11113     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11114   },
11115   {
11116     TYPE_KEY_X11,
11117     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11118   },
11119   {
11120     TYPE_KEY_X11,
11121     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11122   },
11123   {
11124     TYPE_BOOLEAN,
11125     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11126   {
11127     TYPE_BOOLEAN,
11128     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11129   },
11130   {
11131     TYPE_BOOLEAN,
11132     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11133   },
11134   {
11135     TYPE_SWITCH3,
11136     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11137   },
11138   {
11139     TYPE_INTEGER,
11140     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11141   },
11142 };
11143
11144 static struct TokenInfo options_setup_tokens[] =
11145 {
11146   {
11147     TYPE_BOOLEAN,
11148     &setup.options.verbose,                     "options.verbose"
11149   },
11150   {
11151     TYPE_BOOLEAN,
11152     &setup.options.debug,                       "options.debug"
11153   },
11154   {
11155     TYPE_STRING,
11156     &setup.options.debug_mode,                  "options.debug_mode"
11157   },
11158 };
11159
11160 static void setSetupInfoToDefaults(struct SetupInfo *si)
11161 {
11162   int i;
11163
11164   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11165
11166   si->multiple_users = TRUE;
11167
11168   si->sound = TRUE;
11169   si->sound_loops = TRUE;
11170   si->sound_music = TRUE;
11171   si->sound_simple = TRUE;
11172   si->toons = TRUE;
11173   si->global_animations = TRUE;
11174   si->scroll_delay = TRUE;
11175   si->forced_scroll_delay = FALSE;
11176   si->scroll_delay_value = STD_SCROLL_DELAY;
11177   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11178   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11179   si->fade_screens = TRUE;
11180   si->autorecord = TRUE;
11181   si->autorecord_after_replay = TRUE;
11182   si->auto_pause_on_start = FALSE;
11183   si->show_titlescreen = TRUE;
11184   si->quick_doors = FALSE;
11185   si->team_mode = FALSE;
11186   si->handicap = TRUE;
11187   si->skip_levels = TRUE;
11188   si->increment_levels = TRUE;
11189   si->auto_play_next_level = TRUE;
11190   si->count_score_after_game = TRUE;
11191   si->show_scores_after_game = TRUE;
11192   si->time_limit = TRUE;
11193   si->fullscreen = FALSE;
11194   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11195   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11196   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11197   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11198   si->ask_on_escape = TRUE;
11199   si->ask_on_escape_editor = TRUE;
11200   si->ask_on_game_over = TRUE;
11201   si->ask_on_quit_game = TRUE;
11202   si->ask_on_quit_program = TRUE;
11203   si->quick_switch = FALSE;
11204   si->input_on_focus = FALSE;
11205   si->prefer_aga_graphics = TRUE;
11206   si->prefer_lowpass_sounds = FALSE;
11207   si->prefer_extra_panel_items = TRUE;
11208   si->game_speed_extended = FALSE;
11209   si->game_frame_delay = GAME_FRAME_DELAY;
11210   si->bd_skip_uncovering = FALSE;
11211   si->bd_skip_hatching = FALSE;
11212   si->bd_scroll_delay = TRUE;
11213   si->bd_smooth_movements = AUTO;
11214   si->sp_show_border_elements = FALSE;
11215   si->small_game_graphics = FALSE;
11216   si->show_load_save_buttons = FALSE;
11217   si->show_undo_redo_buttons = FALSE;
11218   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11219
11220   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11221   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11222   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11223
11224   si->override_level_graphics = FALSE;
11225   si->override_level_sounds = FALSE;
11226   si->override_level_music = FALSE;
11227
11228   si->volume_simple = 100;              // percent
11229   si->volume_loops = 100;               // percent
11230   si->volume_music = 100;               // percent
11231
11232   si->network_mode = FALSE;
11233   si->network_player_nr = 0;            // first player
11234   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11235
11236   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11237   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11238   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11239   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11240   si->touch.draw_outlined = TRUE;
11241   si->touch.draw_pressed = TRUE;
11242
11243   for (i = 0; i < 2; i++)
11244   {
11245     char *default_grid_button[6][2] =
11246     {
11247       { "      ", "  ^^  " },
11248       { "      ", "  ^^  " },
11249       { "      ", "<<  >>" },
11250       { "      ", "<<  >>" },
11251       { "111222", "  vv  " },
11252       { "111222", "  vv  " }
11253     };
11254     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11255     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11256     int min_xsize = MIN(6, grid_xsize);
11257     int min_ysize = MIN(6, grid_ysize);
11258     int startx = grid_xsize - min_xsize;
11259     int starty = grid_ysize - min_ysize;
11260     int x, y;
11261
11262     // virtual buttons grid can only be set to defaults if video is initialized
11263     // (this will be repeated if virtual buttons are not loaded from setup file)
11264     if (video.initialized)
11265     {
11266       si->touch.grid_xsize[i] = grid_xsize;
11267       si->touch.grid_ysize[i] = grid_ysize;
11268     }
11269     else
11270     {
11271       si->touch.grid_xsize[i] = -1;
11272       si->touch.grid_ysize[i] = -1;
11273     }
11274
11275     for (x = 0; x < MAX_GRID_XSIZE; x++)
11276       for (y = 0; y < MAX_GRID_YSIZE; y++)
11277         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11278
11279     for (x = 0; x < min_xsize; x++)
11280       for (y = 0; y < min_ysize; y++)
11281         si->touch.grid_button[i][x][starty + y] =
11282           default_grid_button[y][0][x];
11283
11284     for (x = 0; x < min_xsize; x++)
11285       for (y = 0; y < min_ysize; y++)
11286         si->touch.grid_button[i][startx + x][starty + y] =
11287           default_grid_button[y][1][x];
11288   }
11289
11290   si->touch.grid_initialized            = video.initialized;
11291
11292   si->touch.overlay_buttons             = FALSE;
11293
11294   si->editor.el_boulderdash             = TRUE;
11295   si->editor.el_boulderdash_native      = TRUE;
11296   si->editor.el_boulderdash_effects     = TRUE;
11297   si->editor.el_emerald_mine            = TRUE;
11298   si->editor.el_emerald_mine_club       = TRUE;
11299   si->editor.el_more                    = TRUE;
11300   si->editor.el_sokoban                 = TRUE;
11301   si->editor.el_supaplex                = TRUE;
11302   si->editor.el_diamond_caves           = TRUE;
11303   si->editor.el_dx_boulderdash          = TRUE;
11304
11305   si->editor.el_mirror_magic            = TRUE;
11306   si->editor.el_deflektor               = TRUE;
11307
11308   si->editor.el_chars                   = TRUE;
11309   si->editor.el_steel_chars             = TRUE;
11310
11311   si->editor.el_classic                 = TRUE;
11312   si->editor.el_custom                  = TRUE;
11313
11314   si->editor.el_user_defined            = FALSE;
11315   si->editor.el_dynamic                 = TRUE;
11316
11317   si->editor.el_headlines               = TRUE;
11318
11319   si->editor.show_element_token         = FALSE;
11320
11321   si->editor.show_read_only_warning     = TRUE;
11322
11323   si->editor.use_template_for_new_levels = TRUE;
11324
11325   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11326   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11327   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11328   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11329   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11330
11331   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11332   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11333   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11334   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11335   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11336
11337   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11338   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11339   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11340   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11341   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11342   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11343
11344   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11345   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11346   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11347
11348   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11349   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11350   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11351   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11352
11353   for (i = 0; i < MAX_PLAYERS; i++)
11354   {
11355     si->input[i].use_joystick = FALSE;
11356     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11357     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11358     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11359     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11360     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11361     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11362     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11363     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11364     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11365     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11366     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11367     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11368     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11369     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11370     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11371   }
11372
11373   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11374   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11375   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11376   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11377
11378   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11379   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11380   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11381   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11382   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11383   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11384   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11385
11386   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11387
11388   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11389   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11390   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11391
11392   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11393   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11394   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11395
11396   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11397   si->internal.choose_from_top_leveldir = FALSE;
11398   si->internal.show_scaling_in_title = TRUE;
11399   si->internal.create_user_levelset = TRUE;
11400   si->internal.info_screens_from_main = FALSE;
11401
11402   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11403   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11404
11405   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11406   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11407   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11408   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11409   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11410   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11411   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11412   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11413   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11414   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11415
11416   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11417   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11418   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11419   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11420   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11421   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11422   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11423   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11424   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11425   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11426
11427   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11428   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11429
11430   si->debug.show_frames_per_second = FALSE;
11431
11432   si->debug.xsn_mode = AUTO;
11433   si->debug.xsn_percent = 0;
11434
11435   si->options.verbose = FALSE;
11436   si->options.debug = FALSE;
11437   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11438
11439 #if defined(PLATFORM_ANDROID)
11440   si->fullscreen = TRUE;
11441   si->touch.overlay_buttons = TRUE;
11442 #endif
11443
11444   setHideSetupEntry(&setup.debug.xsn_mode);
11445 }
11446
11447 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11448 {
11449   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11450 }
11451
11452 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11453 {
11454   si->player_uuid = NULL;       // (will be set later)
11455   si->player_version = 1;       // (will be set later)
11456
11457   si->use_api_server = TRUE;
11458   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11459   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11460   si->ask_for_uploading_tapes = TRUE;
11461   si->ask_for_remaining_tapes = FALSE;
11462   si->provide_uploading_tapes = TRUE;
11463   si->ask_for_using_api_server = TRUE;
11464   si->has_remaining_tapes = FALSE;
11465 }
11466
11467 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11468 {
11469   si->editor_cascade.el_bd              = TRUE;
11470   si->editor_cascade.el_bd_native       = TRUE;
11471   si->editor_cascade.el_bd_effects      = FALSE;
11472   si->editor_cascade.el_em              = TRUE;
11473   si->editor_cascade.el_emc             = TRUE;
11474   si->editor_cascade.el_rnd             = TRUE;
11475   si->editor_cascade.el_sb              = TRUE;
11476   si->editor_cascade.el_sp              = TRUE;
11477   si->editor_cascade.el_dc              = TRUE;
11478   si->editor_cascade.el_dx              = TRUE;
11479
11480   si->editor_cascade.el_mm              = TRUE;
11481   si->editor_cascade.el_df              = TRUE;
11482
11483   si->editor_cascade.el_chars           = FALSE;
11484   si->editor_cascade.el_steel_chars     = FALSE;
11485   si->editor_cascade.el_ce              = FALSE;
11486   si->editor_cascade.el_ge              = FALSE;
11487   si->editor_cascade.el_es              = FALSE;
11488   si->editor_cascade.el_ref             = FALSE;
11489   si->editor_cascade.el_user            = FALSE;
11490   si->editor_cascade.el_dynamic         = FALSE;
11491 }
11492
11493 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11494
11495 static char *getHideSetupToken(void *setup_value)
11496 {
11497   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11498
11499   if (setup_value != NULL)
11500     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11501
11502   return hide_setup_token;
11503 }
11504
11505 void setHideSetupEntry(void *setup_value)
11506 {
11507   char *hide_setup_token = getHideSetupToken(setup_value);
11508
11509   if (hide_setup_hash == NULL)
11510     hide_setup_hash = newSetupFileHash();
11511
11512   if (setup_value != NULL)
11513     setHashEntry(hide_setup_hash, hide_setup_token, "");
11514 }
11515
11516 void removeHideSetupEntry(void *setup_value)
11517 {
11518   char *hide_setup_token = getHideSetupToken(setup_value);
11519
11520   if (setup_value != NULL)
11521     removeHashEntry(hide_setup_hash, hide_setup_token);
11522 }
11523
11524 boolean hideSetupEntry(void *setup_value)
11525 {
11526   char *hide_setup_token = getHideSetupToken(setup_value);
11527
11528   return (setup_value != NULL &&
11529           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11530 }
11531
11532 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11533                                       struct TokenInfo *token_info,
11534                                       int token_nr, char *token_text)
11535 {
11536   char *token_hide_text = getStringCat2(token_text, ".hide");
11537   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11538
11539   // set the value of this setup option in the setup option structure
11540   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11541
11542   // check if this setup option should be hidden in the setup menu
11543   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11544     setHideSetupEntry(token_info[token_nr].value);
11545
11546   free(token_hide_text);
11547 }
11548
11549 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11550                                       struct TokenInfo *token_info,
11551                                       int token_nr)
11552 {
11553   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11554                             token_info[token_nr].text);
11555 }
11556
11557 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11558 {
11559   int i, pnr;
11560
11561   if (!setup_file_hash)
11562     return;
11563
11564   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11565     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11566
11567   setup.touch.grid_initialized = TRUE;
11568   for (i = 0; i < 2; i++)
11569   {
11570     int grid_xsize = setup.touch.grid_xsize[i];
11571     int grid_ysize = setup.touch.grid_ysize[i];
11572     int x, y;
11573
11574     // if virtual buttons are not loaded from setup file, repeat initializing
11575     // virtual buttons grid with default values later when video is initialized
11576     if (grid_xsize == -1 ||
11577         grid_ysize == -1)
11578     {
11579       setup.touch.grid_initialized = FALSE;
11580
11581       continue;
11582     }
11583
11584     for (y = 0; y < grid_ysize; y++)
11585     {
11586       char token_string[MAX_LINE_LEN];
11587
11588       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11589
11590       char *value_string = getHashEntry(setup_file_hash, token_string);
11591
11592       if (value_string == NULL)
11593         continue;
11594
11595       for (x = 0; x < grid_xsize; x++)
11596       {
11597         char c = value_string[x];
11598
11599         setup.touch.grid_button[i][x][y] =
11600           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11601       }
11602     }
11603   }
11604
11605   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11606     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11607
11608   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11609     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11610
11611   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11612   {
11613     char prefix[30];
11614
11615     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11616
11617     setup_input = setup.input[pnr];
11618     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11619     {
11620       char full_token[100];
11621
11622       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11623       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11624                                 full_token);
11625     }
11626     setup.input[pnr] = setup_input;
11627   }
11628
11629   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11630     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11631
11632   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11633     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11634
11635   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11636     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11637
11638   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11639     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11640
11641   setHideRelatedSetupEntries();
11642 }
11643
11644 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11645 {
11646   int i;
11647
11648   if (!setup_file_hash)
11649     return;
11650
11651   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11652     setSetupInfo(auto_setup_tokens, i,
11653                  getHashEntry(setup_file_hash,
11654                               auto_setup_tokens[i].text));
11655 }
11656
11657 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11658 {
11659   int i;
11660
11661   if (!setup_file_hash)
11662     return;
11663
11664   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11665     setSetupInfo(server_setup_tokens, i,
11666                  getHashEntry(setup_file_hash,
11667                               server_setup_tokens[i].text));
11668 }
11669
11670 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11671 {
11672   int i;
11673
11674   if (!setup_file_hash)
11675     return;
11676
11677   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11678     setSetupInfo(editor_cascade_setup_tokens, i,
11679                  getHashEntry(setup_file_hash,
11680                               editor_cascade_setup_tokens[i].text));
11681 }
11682
11683 void LoadUserNames(void)
11684 {
11685   int last_user_nr = user.nr;
11686   int i;
11687
11688   if (global.user_names != NULL)
11689   {
11690     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11691       checked_free(global.user_names[i]);
11692
11693     checked_free(global.user_names);
11694   }
11695
11696   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11697
11698   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11699   {
11700     user.nr = i;
11701
11702     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11703
11704     if (setup_file_hash)
11705     {
11706       char *player_name = getHashEntry(setup_file_hash, "player_name");
11707
11708       global.user_names[i] = getFixedUserName(player_name);
11709
11710       freeSetupFileHash(setup_file_hash);
11711     }
11712
11713     if (global.user_names[i] == NULL)
11714       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11715   }
11716
11717   user.nr = last_user_nr;
11718 }
11719
11720 void LoadSetupFromFilename(char *filename)
11721 {
11722   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11723
11724   if (setup_file_hash)
11725   {
11726     decodeSetupFileHash_Default(setup_file_hash);
11727
11728     freeSetupFileHash(setup_file_hash);
11729   }
11730   else
11731   {
11732     Debug("setup", "using default setup values");
11733   }
11734 }
11735
11736 static void LoadSetup_SpecialPostProcessing(void)
11737 {
11738   char *player_name_new;
11739
11740   // needed to work around problems with fixed length strings
11741   player_name_new = getFixedUserName(setup.player_name);
11742   free(setup.player_name);
11743   setup.player_name = player_name_new;
11744
11745   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11746   if (setup.scroll_delay == FALSE)
11747   {
11748     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11749     setup.scroll_delay = TRUE;                  // now always "on"
11750   }
11751
11752   // make sure that scroll delay value stays inside valid range
11753   setup.scroll_delay_value =
11754     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11755 }
11756
11757 void LoadSetup_Default(void)
11758 {
11759   char *filename;
11760
11761   // always start with reliable default values
11762   setSetupInfoToDefaults(&setup);
11763
11764   // try to load setup values from default setup file
11765   filename = getDefaultSetupFilename();
11766
11767   if (fileExists(filename))
11768     LoadSetupFromFilename(filename);
11769
11770   // try to load setup values from platform setup file
11771   filename = getPlatformSetupFilename();
11772
11773   if (fileExists(filename))
11774     LoadSetupFromFilename(filename);
11775
11776   // try to load setup values from user setup file
11777   filename = getSetupFilename();
11778
11779   LoadSetupFromFilename(filename);
11780
11781   LoadSetup_SpecialPostProcessing();
11782 }
11783
11784 void LoadSetup_AutoSetup(void)
11785 {
11786   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11787   SetupFileHash *setup_file_hash = NULL;
11788
11789   // always start with reliable default values
11790   setSetupInfoToDefaults_AutoSetup(&setup);
11791
11792   setup_file_hash = loadSetupFileHash(filename);
11793
11794   if (setup_file_hash)
11795   {
11796     decodeSetupFileHash_AutoSetup(setup_file_hash);
11797
11798     freeSetupFileHash(setup_file_hash);
11799   }
11800
11801   free(filename);
11802 }
11803
11804 void LoadSetup_ServerSetup(void)
11805 {
11806   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11807   SetupFileHash *setup_file_hash = NULL;
11808
11809   // always start with reliable default values
11810   setSetupInfoToDefaults_ServerSetup(&setup);
11811
11812   setup_file_hash = loadSetupFileHash(filename);
11813
11814   if (setup_file_hash)
11815   {
11816     decodeSetupFileHash_ServerSetup(setup_file_hash);
11817
11818     freeSetupFileHash(setup_file_hash);
11819   }
11820
11821   free(filename);
11822
11823   if (setup.player_uuid == NULL)
11824   {
11825     // player UUID does not yet exist in setup file
11826     setup.player_uuid = getStringCopy(getUUID());
11827     setup.player_version = 2;
11828
11829     SaveSetup_ServerSetup();
11830   }
11831 }
11832
11833 void LoadSetup_EditorCascade(void)
11834 {
11835   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11836   SetupFileHash *setup_file_hash = NULL;
11837
11838   // always start with reliable default values
11839   setSetupInfoToDefaults_EditorCascade(&setup);
11840
11841   setup_file_hash = loadSetupFileHash(filename);
11842
11843   if (setup_file_hash)
11844   {
11845     decodeSetupFileHash_EditorCascade(setup_file_hash);
11846
11847     freeSetupFileHash(setup_file_hash);
11848   }
11849
11850   free(filename);
11851 }
11852
11853 void LoadSetup(void)
11854 {
11855   LoadSetup_Default();
11856   LoadSetup_AutoSetup();
11857   LoadSetup_ServerSetup();
11858   LoadSetup_EditorCascade();
11859 }
11860
11861 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11862                                            char *mapping_line)
11863 {
11864   char mapping_guid[MAX_LINE_LEN];
11865   char *mapping_start, *mapping_end;
11866
11867   // get GUID from game controller mapping line: copy complete line
11868   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11869   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11870
11871   // get GUID from game controller mapping line: cut after GUID part
11872   mapping_start = strchr(mapping_guid, ',');
11873   if (mapping_start != NULL)
11874     *mapping_start = '\0';
11875
11876   // cut newline from game controller mapping line
11877   mapping_end = strchr(mapping_line, '\n');
11878   if (mapping_end != NULL)
11879     *mapping_end = '\0';
11880
11881   // add mapping entry to game controller mappings hash
11882   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11883 }
11884
11885 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11886                                                  char *filename)
11887 {
11888   FILE *file;
11889
11890   if (!(file = fopen(filename, MODE_READ)))
11891   {
11892     Warn("cannot read game controller mappings file '%s'", filename);
11893
11894     return;
11895   }
11896
11897   while (!feof(file))
11898   {
11899     char line[MAX_LINE_LEN];
11900
11901     if (!fgets(line, MAX_LINE_LEN, file))
11902       break;
11903
11904     addGameControllerMappingToHash(mappings_hash, line);
11905   }
11906
11907   fclose(file);
11908 }
11909
11910 void SaveSetup_Default(void)
11911 {
11912   char *filename = getSetupFilename();
11913   FILE *file;
11914   int i, pnr;
11915
11916   InitUserDataDirectory();
11917
11918   if (!(file = fopen(filename, MODE_WRITE)))
11919   {
11920     Warn("cannot write setup file '%s'", filename);
11921
11922     return;
11923   }
11924
11925   fprintFileHeader(file, SETUP_FILENAME);
11926
11927   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11928   {
11929     // just to make things nicer :)
11930     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11931         global_setup_tokens[i].value == &setup.sound                    ||
11932         global_setup_tokens[i].value == &setup.graphics_set             ||
11933         global_setup_tokens[i].value == &setup.volume_simple            ||
11934         global_setup_tokens[i].value == &setup.network_mode             ||
11935         global_setup_tokens[i].value == &setup.touch.control_type       ||
11936         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
11937         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11938       fprintf(file, "\n");
11939
11940     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11941   }
11942
11943   for (i = 0; i < 2; i++)
11944   {
11945     int grid_xsize = setup.touch.grid_xsize[i];
11946     int grid_ysize = setup.touch.grid_ysize[i];
11947     int x, y;
11948
11949     fprintf(file, "\n");
11950
11951     for (y = 0; y < grid_ysize; y++)
11952     {
11953       char token_string[MAX_LINE_LEN];
11954       char value_string[MAX_LINE_LEN];
11955
11956       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11957
11958       for (x = 0; x < grid_xsize; x++)
11959       {
11960         char c = setup.touch.grid_button[i][x][y];
11961
11962         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11963       }
11964
11965       value_string[grid_xsize] = '\0';
11966
11967       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11968     }
11969   }
11970
11971   fprintf(file, "\n");
11972   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11973     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11974
11975   fprintf(file, "\n");
11976   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11977     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11978
11979   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11980   {
11981     char prefix[30];
11982
11983     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11984     fprintf(file, "\n");
11985
11986     setup_input = setup.input[pnr];
11987     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11988       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11989   }
11990
11991   fprintf(file, "\n");
11992   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11993     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11994
11995   // (internal setup values not saved to user setup file)
11996
11997   fprintf(file, "\n");
11998   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11999     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12000         setup.debug.xsn_mode != AUTO)
12001       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12002
12003   fprintf(file, "\n");
12004   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12005     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12006
12007   fclose(file);
12008
12009   SetFilePermissions(filename, PERMS_PRIVATE);
12010 }
12011
12012 void SaveSetup_AutoSetup(void)
12013 {
12014   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12015   FILE *file;
12016   int i;
12017
12018   InitUserDataDirectory();
12019
12020   if (!(file = fopen(filename, MODE_WRITE)))
12021   {
12022     Warn("cannot write auto setup file '%s'", filename);
12023
12024     free(filename);
12025
12026     return;
12027   }
12028
12029   fprintFileHeader(file, AUTOSETUP_FILENAME);
12030
12031   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12032     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12033
12034   fclose(file);
12035
12036   SetFilePermissions(filename, PERMS_PRIVATE);
12037
12038   free(filename);
12039 }
12040
12041 void SaveSetup_ServerSetup(void)
12042 {
12043   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12044   FILE *file;
12045   int i;
12046
12047   InitUserDataDirectory();
12048
12049   if (!(file = fopen(filename, MODE_WRITE)))
12050   {
12051     Warn("cannot write server setup file '%s'", filename);
12052
12053     free(filename);
12054
12055     return;
12056   }
12057
12058   fprintFileHeader(file, SERVERSETUP_FILENAME);
12059
12060   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12061   {
12062     // just to make things nicer :)
12063     if (server_setup_tokens[i].value == &setup.use_api_server)
12064       fprintf(file, "\n");
12065
12066     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12067   }
12068
12069   fclose(file);
12070
12071   SetFilePermissions(filename, PERMS_PRIVATE);
12072
12073   free(filename);
12074 }
12075
12076 void SaveSetup_EditorCascade(void)
12077 {
12078   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12079   FILE *file;
12080   int i;
12081
12082   InitUserDataDirectory();
12083
12084   if (!(file = fopen(filename, MODE_WRITE)))
12085   {
12086     Warn("cannot write editor cascade state file '%s'", filename);
12087
12088     free(filename);
12089
12090     return;
12091   }
12092
12093   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12094
12095   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12096     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12097
12098   fclose(file);
12099
12100   SetFilePermissions(filename, PERMS_PRIVATE);
12101
12102   free(filename);
12103 }
12104
12105 void SaveSetup(void)
12106 {
12107   SaveSetup_Default();
12108   SaveSetup_AutoSetup();
12109   SaveSetup_ServerSetup();
12110   SaveSetup_EditorCascade();
12111 }
12112
12113 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12114                                                   char *filename)
12115 {
12116   FILE *file;
12117
12118   if (!(file = fopen(filename, MODE_WRITE)))
12119   {
12120     Warn("cannot write game controller mappings file '%s'", filename);
12121
12122     return;
12123   }
12124
12125   BEGIN_HASH_ITERATION(mappings_hash, itr)
12126   {
12127     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12128   }
12129   END_HASH_ITERATION(mappings_hash, itr)
12130
12131   fclose(file);
12132 }
12133
12134 void SaveSetup_AddGameControllerMapping(char *mapping)
12135 {
12136   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12137   SetupFileHash *mappings_hash = newSetupFileHash();
12138
12139   InitUserDataDirectory();
12140
12141   // load existing personal game controller mappings
12142   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12143
12144   // add new mapping to personal game controller mappings
12145   addGameControllerMappingToHash(mappings_hash, mapping);
12146
12147   // save updated personal game controller mappings
12148   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12149
12150   freeSetupFileHash(mappings_hash);
12151   free(filename);
12152 }
12153
12154 void LoadCustomElementDescriptions(void)
12155 {
12156   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12157   SetupFileHash *setup_file_hash;
12158   int i;
12159
12160   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12161   {
12162     if (element_info[i].custom_description != NULL)
12163     {
12164       free(element_info[i].custom_description);
12165       element_info[i].custom_description = NULL;
12166     }
12167   }
12168
12169   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12170     return;
12171
12172   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12173   {
12174     char *token = getStringCat2(element_info[i].token_name, ".name");
12175     char *value = getHashEntry(setup_file_hash, token);
12176
12177     if (value != NULL)
12178       element_info[i].custom_description = getStringCopy(value);
12179
12180     free(token);
12181   }
12182
12183   freeSetupFileHash(setup_file_hash);
12184 }
12185
12186 static int getElementFromToken(char *token)
12187 {
12188   char *value = getHashEntry(element_token_hash, token);
12189
12190   if (value != NULL)
12191     return atoi(value);
12192
12193   Warn("unknown element token '%s'", token);
12194
12195   return EL_UNDEFINED;
12196 }
12197
12198 void FreeGlobalAnimEventInfo(void)
12199 {
12200   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12201
12202   if (gaei->event_list == NULL)
12203     return;
12204
12205   int i;
12206
12207   for (i = 0; i < gaei->num_event_lists; i++)
12208   {
12209     checked_free(gaei->event_list[i]->event_value);
12210     checked_free(gaei->event_list[i]);
12211   }
12212
12213   checked_free(gaei->event_list);
12214
12215   gaei->event_list = NULL;
12216   gaei->num_event_lists = 0;
12217 }
12218
12219 static int AddGlobalAnimEventList(void)
12220 {
12221   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12222   int list_pos = gaei->num_event_lists++;
12223
12224   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12225                                      sizeof(struct GlobalAnimEventListInfo *));
12226
12227   gaei->event_list[list_pos] =
12228     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12229
12230   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12231
12232   gaeli->event_value = NULL;
12233   gaeli->num_event_values = 0;
12234
12235   return list_pos;
12236 }
12237
12238 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12239 {
12240   // do not add empty global animation events
12241   if (event_value == ANIM_EVENT_NONE)
12242     return list_pos;
12243
12244   // if list position is undefined, create new list
12245   if (list_pos == ANIM_EVENT_UNDEFINED)
12246     list_pos = AddGlobalAnimEventList();
12247
12248   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12249   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12250   int value_pos = gaeli->num_event_values++;
12251
12252   gaeli->event_value = checked_realloc(gaeli->event_value,
12253                                        gaeli->num_event_values * sizeof(int *));
12254
12255   gaeli->event_value[value_pos] = event_value;
12256
12257   return list_pos;
12258 }
12259
12260 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12261 {
12262   if (list_pos == ANIM_EVENT_UNDEFINED)
12263     return ANIM_EVENT_NONE;
12264
12265   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12266   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12267
12268   return gaeli->event_value[value_pos];
12269 }
12270
12271 int GetGlobalAnimEventValueCount(int list_pos)
12272 {
12273   if (list_pos == ANIM_EVENT_UNDEFINED)
12274     return 0;
12275
12276   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12277   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12278
12279   return gaeli->num_event_values;
12280 }
12281
12282 // This function checks if a string <s> of the format "string1, string2, ..."
12283 // exactly contains a string <s_contained>.
12284
12285 static boolean string_has_parameter(char *s, char *s_contained)
12286 {
12287   char *substring;
12288
12289   if (s == NULL || s_contained == NULL)
12290     return FALSE;
12291
12292   if (strlen(s_contained) > strlen(s))
12293     return FALSE;
12294
12295   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12296   {
12297     char next_char = s[strlen(s_contained)];
12298
12299     // check if next character is delimiter or whitespace
12300     if (next_char == ',' || next_char == '\0' ||
12301         next_char == ' ' || next_char == '\t')
12302       return TRUE;
12303   }
12304
12305   // check if string contains another parameter string after a comma
12306   substring = strchr(s, ',');
12307   if (substring == NULL)        // string does not contain a comma
12308     return FALSE;
12309
12310   // advance string pointer to next character after the comma
12311   substring++;
12312
12313   // skip potential whitespaces after the comma
12314   while (*substring == ' ' || *substring == '\t')
12315     substring++;
12316
12317   return string_has_parameter(substring, s_contained);
12318 }
12319
12320 static int get_anim_parameter_value_ce(char *s)
12321 {
12322   char *s_ptr = s;
12323   char *pattern_1 = "ce_change:custom_";
12324   char *pattern_2 = ".page_";
12325   int pattern_1_len = strlen(pattern_1);
12326   char *matching_char = strstr(s_ptr, pattern_1);
12327   int result = ANIM_EVENT_NONE;
12328
12329   if (matching_char == NULL)
12330     return ANIM_EVENT_NONE;
12331
12332   result = ANIM_EVENT_CE_CHANGE;
12333
12334   s_ptr = matching_char + pattern_1_len;
12335
12336   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12337   if (*s_ptr >= '0' && *s_ptr <= '9')
12338   {
12339     int gic_ce_nr = (*s_ptr++ - '0');
12340
12341     if (*s_ptr >= '0' && *s_ptr <= '9')
12342     {
12343       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12344
12345       if (*s_ptr >= '0' && *s_ptr <= '9')
12346         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12347     }
12348
12349     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12350       return ANIM_EVENT_NONE;
12351
12352     // custom element stored as 0 to 255
12353     gic_ce_nr--;
12354
12355     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12356   }
12357   else
12358   {
12359     // invalid custom element number specified
12360
12361     return ANIM_EVENT_NONE;
12362   }
12363
12364   // check for change page number ("page_X" or "page_XX") (optional)
12365   if (strPrefix(s_ptr, pattern_2))
12366   {
12367     s_ptr += strlen(pattern_2);
12368
12369     if (*s_ptr >= '0' && *s_ptr <= '9')
12370     {
12371       int gic_page_nr = (*s_ptr++ - '0');
12372
12373       if (*s_ptr >= '0' && *s_ptr <= '9')
12374         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12375
12376       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12377         return ANIM_EVENT_NONE;
12378
12379       // change page stored as 1 to 32 (0 means "all change pages")
12380
12381       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12382     }
12383     else
12384     {
12385       // invalid animation part number specified
12386
12387       return ANIM_EVENT_NONE;
12388     }
12389   }
12390
12391   // discard result if next character is neither delimiter nor whitespace
12392   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12393         *s_ptr == ' ' || *s_ptr == '\t'))
12394     return ANIM_EVENT_NONE;
12395
12396   return result;
12397 }
12398
12399 static int get_anim_parameter_value(char *s)
12400 {
12401   int event_value[] =
12402   {
12403     ANIM_EVENT_CLICK,
12404     ANIM_EVENT_INIT,
12405     ANIM_EVENT_START,
12406     ANIM_EVENT_END,
12407     ANIM_EVENT_POST
12408   };
12409   char *pattern_1[] =
12410   {
12411     "click:anim_",
12412     "init:anim_",
12413     "start:anim_",
12414     "end:anim_",
12415     "post:anim_"
12416   };
12417   char *pattern_2 = ".part_";
12418   char *matching_char = NULL;
12419   char *s_ptr = s;
12420   int pattern_1_len = 0;
12421   int result = ANIM_EVENT_NONE;
12422   int i;
12423
12424   result = get_anim_parameter_value_ce(s);
12425
12426   if (result != ANIM_EVENT_NONE)
12427     return result;
12428
12429   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12430   {
12431     matching_char = strstr(s_ptr, pattern_1[i]);
12432     pattern_1_len = strlen(pattern_1[i]);
12433     result = event_value[i];
12434
12435     if (matching_char != NULL)
12436       break;
12437   }
12438
12439   if (matching_char == NULL)
12440     return ANIM_EVENT_NONE;
12441
12442   s_ptr = matching_char + pattern_1_len;
12443
12444   // check for main animation number ("anim_X" or "anim_XX")
12445   if (*s_ptr >= '0' && *s_ptr <= '9')
12446   {
12447     int gic_anim_nr = (*s_ptr++ - '0');
12448
12449     if (*s_ptr >= '0' && *s_ptr <= '9')
12450       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12451
12452     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12453       return ANIM_EVENT_NONE;
12454
12455     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12456   }
12457   else
12458   {
12459     // invalid main animation number specified
12460
12461     return ANIM_EVENT_NONE;
12462   }
12463
12464   // check for animation part number ("part_X" or "part_XX") (optional)
12465   if (strPrefix(s_ptr, pattern_2))
12466   {
12467     s_ptr += strlen(pattern_2);
12468
12469     if (*s_ptr >= '0' && *s_ptr <= '9')
12470     {
12471       int gic_part_nr = (*s_ptr++ - '0');
12472
12473       if (*s_ptr >= '0' && *s_ptr <= '9')
12474         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12475
12476       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12477         return ANIM_EVENT_NONE;
12478
12479       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12480     }
12481     else
12482     {
12483       // invalid animation part number specified
12484
12485       return ANIM_EVENT_NONE;
12486     }
12487   }
12488
12489   // discard result if next character is neither delimiter nor whitespace
12490   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12491         *s_ptr == ' ' || *s_ptr == '\t'))
12492     return ANIM_EVENT_NONE;
12493
12494   return result;
12495 }
12496
12497 static int get_anim_parameter_values(char *s)
12498 {
12499   int list_pos = ANIM_EVENT_UNDEFINED;
12500   int event_value = ANIM_EVENT_DEFAULT;
12501
12502   if (string_has_parameter(s, "any"))
12503     event_value |= ANIM_EVENT_ANY;
12504
12505   if (string_has_parameter(s, "click:self") ||
12506       string_has_parameter(s, "click") ||
12507       string_has_parameter(s, "self"))
12508     event_value |= ANIM_EVENT_SELF;
12509
12510   if (string_has_parameter(s, "unclick:any"))
12511     event_value |= ANIM_EVENT_UNCLICK_ANY;
12512
12513   // if animation event found, add it to global animation event list
12514   if (event_value != ANIM_EVENT_NONE)
12515     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12516
12517   while (s != NULL)
12518   {
12519     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12520     event_value = get_anim_parameter_value(s);
12521
12522     // if animation event found, add it to global animation event list
12523     if (event_value != ANIM_EVENT_NONE)
12524       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12525
12526     // continue with next part of the string, starting with next comma
12527     s = strchr(s + 1, ',');
12528   }
12529
12530   return list_pos;
12531 }
12532
12533 static int get_anim_action_parameter_value(char *token)
12534 {
12535   // check most common default case first to massively speed things up
12536   if (strEqual(token, ARG_UNDEFINED))
12537     return ANIM_EVENT_ACTION_NONE;
12538
12539   int result = getImageIDFromToken(token);
12540
12541   if (result == -1)
12542   {
12543     char *gfx_token = getStringCat2("gfx.", token);
12544
12545     result = getImageIDFromToken(gfx_token);
12546
12547     checked_free(gfx_token);
12548   }
12549
12550   if (result == -1)
12551   {
12552     Key key = getKeyFromX11KeyName(token);
12553
12554     if (key != KSYM_UNDEFINED)
12555       result = -(int)key;
12556   }
12557
12558   if (result == -1)
12559   {
12560     if (isURL(token))
12561     {
12562       result = get_hash_from_string(token);     // unsigned int => int
12563       result = ABS(result);                     // may be negative now
12564       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12565
12566       setHashEntry(anim_url_hash, int2str(result, 0), token);
12567     }
12568   }
12569
12570   if (result == -1)
12571     result = ANIM_EVENT_ACTION_NONE;
12572
12573   return result;
12574 }
12575
12576 int get_parameter_value(char *value_raw, char *suffix, int type)
12577 {
12578   char *value = getStringToLower(value_raw);
12579   int result = 0;       // probably a save default value
12580
12581   if (strEqual(suffix, ".direction"))
12582   {
12583     result = (strEqual(value, "left")  ? MV_LEFT :
12584               strEqual(value, "right") ? MV_RIGHT :
12585               strEqual(value, "up")    ? MV_UP :
12586               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12587   }
12588   else if (strEqual(suffix, ".position"))
12589   {
12590     result = (strEqual(value, "left")   ? POS_LEFT :
12591               strEqual(value, "right")  ? POS_RIGHT :
12592               strEqual(value, "top")    ? POS_TOP :
12593               strEqual(value, "upper")  ? POS_UPPER :
12594               strEqual(value, "middle") ? POS_MIDDLE :
12595               strEqual(value, "lower")  ? POS_LOWER :
12596               strEqual(value, "bottom") ? POS_BOTTOM :
12597               strEqual(value, "any")    ? POS_ANY :
12598               strEqual(value, "ce")     ? POS_CE :
12599               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12600               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12601   }
12602   else if (strEqual(suffix, ".align"))
12603   {
12604     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12605               strEqual(value, "right")  ? ALIGN_RIGHT :
12606               strEqual(value, "center") ? ALIGN_CENTER :
12607               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12608   }
12609   else if (strEqual(suffix, ".valign"))
12610   {
12611     result = (strEqual(value, "top")    ? VALIGN_TOP :
12612               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12613               strEqual(value, "middle") ? VALIGN_MIDDLE :
12614               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12615   }
12616   else if (strEqual(suffix, ".anim_mode"))
12617   {
12618     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12619               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12620               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12621               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12622               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12623               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12624               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12625               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12626               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12627               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12628               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12629               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12630               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12631               string_has_parameter(value, "all")        ? ANIM_ALL :
12632               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12633               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12634               ANIM_DEFAULT);
12635
12636     if (string_has_parameter(value, "once"))
12637       result |= ANIM_ONCE;
12638
12639     if (string_has_parameter(value, "reverse"))
12640       result |= ANIM_REVERSE;
12641
12642     if (string_has_parameter(value, "opaque_player"))
12643       result |= ANIM_OPAQUE_PLAYER;
12644
12645     if (string_has_parameter(value, "static_panel"))
12646       result |= ANIM_STATIC_PANEL;
12647   }
12648   else if (strEqual(suffix, ".init_event") ||
12649            strEqual(suffix, ".anim_event"))
12650   {
12651     result = get_anim_parameter_values(value);
12652   }
12653   else if (strEqual(suffix, ".init_delay_action") ||
12654            strEqual(suffix, ".anim_delay_action") ||
12655            strEqual(suffix, ".post_delay_action") ||
12656            strEqual(suffix, ".init_event_action") ||
12657            strEqual(suffix, ".anim_event_action"))
12658   {
12659     result = get_anim_action_parameter_value(value_raw);
12660   }
12661   else if (strEqual(suffix, ".class"))
12662   {
12663     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12664               get_hash_from_string(value));
12665   }
12666   else if (strEqual(suffix, ".style"))
12667   {
12668     result = STYLE_DEFAULT;
12669
12670     if (string_has_parameter(value, "accurate_borders"))
12671       result |= STYLE_ACCURATE_BORDERS;
12672
12673     if (string_has_parameter(value, "inner_corners"))
12674       result |= STYLE_INNER_CORNERS;
12675
12676     if (string_has_parameter(value, "reverse"))
12677       result |= STYLE_REVERSE;
12678
12679     if (string_has_parameter(value, "leftmost_position"))
12680       result |= STYLE_LEFTMOST_POSITION;
12681
12682     if (string_has_parameter(value, "block_clicks"))
12683       result |= STYLE_BLOCK;
12684
12685     if (string_has_parameter(value, "passthrough_clicks"))
12686       result |= STYLE_PASSTHROUGH;
12687
12688     if (string_has_parameter(value, "multiple_actions"))
12689       result |= STYLE_MULTIPLE_ACTIONS;
12690
12691     if (string_has_parameter(value, "consume_ce_event"))
12692       result |= STYLE_CONSUME_CE_EVENT;
12693   }
12694   else if (strEqual(suffix, ".fade_mode"))
12695   {
12696     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12697               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12698               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12699               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12700               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12701               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12702               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12703               FADE_MODE_DEFAULT);
12704   }
12705   else if (strEqual(suffix, ".auto_delay_unit"))
12706   {
12707     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12708               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12709               AUTO_DELAY_UNIT_DEFAULT);
12710   }
12711   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12712   {
12713     result = gfx.get_font_from_token_function(value);
12714   }
12715   else          // generic parameter of type integer or boolean
12716   {
12717     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12718               type == TYPE_INTEGER ? get_integer_from_string(value) :
12719               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12720               ARG_UNDEFINED_VALUE);
12721   }
12722
12723   free(value);
12724
12725   return result;
12726 }
12727
12728 static int get_token_parameter_value(char *token, char *value_raw)
12729 {
12730   char *suffix;
12731
12732   if (token == NULL || value_raw == NULL)
12733     return ARG_UNDEFINED_VALUE;
12734
12735   suffix = strrchr(token, '.');
12736   if (suffix == NULL)
12737     suffix = token;
12738
12739   if (strEqual(suffix, ".element"))
12740     return getElementFromToken(value_raw);
12741
12742   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12743   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12744 }
12745
12746 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12747                                      boolean ignore_defaults)
12748 {
12749   int i;
12750
12751   for (i = 0; image_config_vars[i].token != NULL; i++)
12752   {
12753     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12754
12755     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12756     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12757       continue;
12758
12759     if (value != NULL)
12760       *image_config_vars[i].value =
12761         get_token_parameter_value(image_config_vars[i].token, value);
12762   }
12763 }
12764
12765 void InitMenuDesignSettings_Static(void)
12766 {
12767   // always start with reliable default values from static default config
12768   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12769 }
12770
12771 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12772 {
12773   int i;
12774
12775   // the following initializes hierarchical values from static configuration
12776
12777   // special case: initialize "ARG_DEFAULT" values in static default config
12778   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12779   titlescreen_initial_first_default.fade_mode  =
12780     title_initial_first_default.fade_mode;
12781   titlescreen_initial_first_default.fade_delay =
12782     title_initial_first_default.fade_delay;
12783   titlescreen_initial_first_default.post_delay =
12784     title_initial_first_default.post_delay;
12785   titlescreen_initial_first_default.auto_delay =
12786     title_initial_first_default.auto_delay;
12787   titlescreen_initial_first_default.auto_delay_unit =
12788     title_initial_first_default.auto_delay_unit;
12789   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12790   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12791   titlescreen_first_default.post_delay = title_first_default.post_delay;
12792   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12793   titlescreen_first_default.auto_delay_unit =
12794     title_first_default.auto_delay_unit;
12795   titlemessage_initial_first_default.fade_mode  =
12796     title_initial_first_default.fade_mode;
12797   titlemessage_initial_first_default.fade_delay =
12798     title_initial_first_default.fade_delay;
12799   titlemessage_initial_first_default.post_delay =
12800     title_initial_first_default.post_delay;
12801   titlemessage_initial_first_default.auto_delay =
12802     title_initial_first_default.auto_delay;
12803   titlemessage_initial_first_default.auto_delay_unit =
12804     title_initial_first_default.auto_delay_unit;
12805   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12806   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12807   titlemessage_first_default.post_delay = title_first_default.post_delay;
12808   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12809   titlemessage_first_default.auto_delay_unit =
12810     title_first_default.auto_delay_unit;
12811
12812   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12813   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12814   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12815   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12816   titlescreen_initial_default.auto_delay_unit =
12817     title_initial_default.auto_delay_unit;
12818   titlescreen_default.fade_mode  = title_default.fade_mode;
12819   titlescreen_default.fade_delay = title_default.fade_delay;
12820   titlescreen_default.post_delay = title_default.post_delay;
12821   titlescreen_default.auto_delay = title_default.auto_delay;
12822   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12823   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12824   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12825   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12826   titlemessage_initial_default.auto_delay_unit =
12827     title_initial_default.auto_delay_unit;
12828   titlemessage_default.fade_mode  = title_default.fade_mode;
12829   titlemessage_default.fade_delay = title_default.fade_delay;
12830   titlemessage_default.post_delay = title_default.post_delay;
12831   titlemessage_default.auto_delay = title_default.auto_delay;
12832   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12833
12834   // special case: initialize "ARG_DEFAULT" values in static default config
12835   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12836   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12837   {
12838     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12839     titlescreen_first[i] = titlescreen_first_default;
12840     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12841     titlemessage_first[i] = titlemessage_first_default;
12842
12843     titlescreen_initial[i] = titlescreen_initial_default;
12844     titlescreen[i] = titlescreen_default;
12845     titlemessage_initial[i] = titlemessage_initial_default;
12846     titlemessage[i] = titlemessage_default;
12847   }
12848
12849   // special case: initialize "ARG_DEFAULT" values in static default config
12850   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12851   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12852   {
12853     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12854       continue;
12855
12856     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12857     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12858     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12859   }
12860
12861   // special case: initialize "ARG_DEFAULT" values in static default config
12862   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12863   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12864   {
12865     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12866     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12867     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12868
12869     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12870       continue;
12871
12872     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12873   }
12874 }
12875
12876 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12877 {
12878   static struct
12879   {
12880     struct XY *dst, *src;
12881   }
12882   game_buttons_xy[] =
12883   {
12884     { &game.button.save,        &game.button.stop       },
12885     { &game.button.pause2,      &game.button.pause      },
12886     { &game.button.load,        &game.button.play       },
12887     { &game.button.undo,        &game.button.stop       },
12888     { &game.button.redo,        &game.button.play       },
12889
12890     { NULL,                     NULL                    }
12891   };
12892   int i, j;
12893
12894   // special case: initialize later added SETUP list size from LEVELS value
12895   if (menu.list_size[GAME_MODE_SETUP] == -1)
12896     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12897
12898   // set default position for snapshot buttons to stop/pause/play buttons
12899   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12900     if ((*game_buttons_xy[i].dst).x == -1 &&
12901         (*game_buttons_xy[i].dst).y == -1)
12902       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12903
12904   // --------------------------------------------------------------------------
12905   // dynamic viewports (including playfield margins, borders and alignments)
12906   // --------------------------------------------------------------------------
12907
12908   // dynamic viewports currently only supported for landscape mode
12909   int display_width  = MAX(video.display_width, video.display_height);
12910   int display_height = MIN(video.display_width, video.display_height);
12911
12912   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12913   {
12914     struct RectWithBorder *vp_window    = &viewport.window[i];
12915     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12916     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12917     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12918     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12919     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12920     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12921     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12922
12923     // adjust window size if min/max width/height is specified
12924
12925     if (vp_window->min_width != -1)
12926     {
12927       int window_width = display_width;
12928
12929       // when using static window height, use aspect ratio of display
12930       if (vp_window->min_height == -1)
12931         window_width = vp_window->height * display_width / display_height;
12932
12933       vp_window->width = MAX(vp_window->min_width, window_width);
12934     }
12935
12936     if (vp_window->min_height != -1)
12937     {
12938       int window_height = display_height;
12939
12940       // when using static window width, use aspect ratio of display
12941       if (vp_window->min_width == -1)
12942         window_height = vp_window->width * display_height / display_width;
12943
12944       vp_window->height = MAX(vp_window->min_height, window_height);
12945     }
12946
12947     if (vp_window->max_width != -1)
12948       vp_window->width = MIN(vp_window->width, vp_window->max_width);
12949
12950     if (vp_window->max_height != -1)
12951       vp_window->height = MIN(vp_window->height, vp_window->max_height);
12952
12953     int playfield_width  = vp_window->width;
12954     int playfield_height = vp_window->height;
12955
12956     // adjust playfield size and position according to specified margins
12957
12958     playfield_width  -= vp_playfield->margin_left;
12959     playfield_width  -= vp_playfield->margin_right;
12960
12961     playfield_height -= vp_playfield->margin_top;
12962     playfield_height -= vp_playfield->margin_bottom;
12963
12964     // adjust playfield size if min/max width/height is specified
12965
12966     if (vp_playfield->min_width != -1)
12967       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12968
12969     if (vp_playfield->min_height != -1)
12970       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12971
12972     if (vp_playfield->max_width != -1)
12973       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12974
12975     if (vp_playfield->max_height != -1)
12976       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12977
12978     // adjust playfield position according to specified alignment
12979
12980     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12981       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12982     else if (vp_playfield->align == ALIGN_CENTER)
12983       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12984     else if (vp_playfield->align == ALIGN_RIGHT)
12985       vp_playfield->x += playfield_width - vp_playfield->width;
12986
12987     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12988       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12989     else if (vp_playfield->valign == VALIGN_MIDDLE)
12990       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12991     else if (vp_playfield->valign == VALIGN_BOTTOM)
12992       vp_playfield->y += playfield_height - vp_playfield->height;
12993
12994     vp_playfield->x += vp_playfield->margin_left;
12995     vp_playfield->y += vp_playfield->margin_top;
12996
12997     // adjust individual playfield borders if only default border is specified
12998
12999     if (vp_playfield->border_left == -1)
13000       vp_playfield->border_left = vp_playfield->border_size;
13001     if (vp_playfield->border_right == -1)
13002       vp_playfield->border_right = vp_playfield->border_size;
13003     if (vp_playfield->border_top == -1)
13004       vp_playfield->border_top = vp_playfield->border_size;
13005     if (vp_playfield->border_bottom == -1)
13006       vp_playfield->border_bottom = vp_playfield->border_size;
13007
13008     // set dynamic playfield borders if borders are specified as undefined
13009     // (but only if window size was dynamic and playfield size was static)
13010
13011     if (dynamic_window_width && !dynamic_playfield_width)
13012     {
13013       if (vp_playfield->border_left == -1)
13014       {
13015         vp_playfield->border_left = (vp_playfield->x -
13016                                      vp_playfield->margin_left);
13017         vp_playfield->x     -= vp_playfield->border_left;
13018         vp_playfield->width += vp_playfield->border_left;
13019       }
13020
13021       if (vp_playfield->border_right == -1)
13022       {
13023         vp_playfield->border_right = (vp_window->width -
13024                                       vp_playfield->x -
13025                                       vp_playfield->width -
13026                                       vp_playfield->margin_right);
13027         vp_playfield->width += vp_playfield->border_right;
13028       }
13029     }
13030
13031     if (dynamic_window_height && !dynamic_playfield_height)
13032     {
13033       if (vp_playfield->border_top == -1)
13034       {
13035         vp_playfield->border_top = (vp_playfield->y -
13036                                     vp_playfield->margin_top);
13037         vp_playfield->y      -= vp_playfield->border_top;
13038         vp_playfield->height += vp_playfield->border_top;
13039       }
13040
13041       if (vp_playfield->border_bottom == -1)
13042       {
13043         vp_playfield->border_bottom = (vp_window->height -
13044                                        vp_playfield->y -
13045                                        vp_playfield->height -
13046                                        vp_playfield->margin_bottom);
13047         vp_playfield->height += vp_playfield->border_bottom;
13048       }
13049     }
13050
13051     // adjust playfield size to be a multiple of a defined alignment tile size
13052
13053     int align_size = vp_playfield->align_size;
13054     int playfield_xtiles = vp_playfield->width  / align_size;
13055     int playfield_ytiles = vp_playfield->height / align_size;
13056     int playfield_width_corrected  = playfield_xtiles * align_size;
13057     int playfield_height_corrected = playfield_ytiles * align_size;
13058     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13059                                  i == GFX_SPECIAL_ARG_EDITOR);
13060
13061     if (is_playfield_mode &&
13062         dynamic_playfield_width &&
13063         vp_playfield->width != playfield_width_corrected)
13064     {
13065       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13066
13067       vp_playfield->width = playfield_width_corrected;
13068
13069       if (vp_playfield->align == ALIGN_LEFT)
13070       {
13071         vp_playfield->border_left += playfield_xdiff;
13072       }
13073       else if (vp_playfield->align == ALIGN_RIGHT)
13074       {
13075         vp_playfield->border_right += playfield_xdiff;
13076       }
13077       else if (vp_playfield->align == ALIGN_CENTER)
13078       {
13079         int border_left_diff  = playfield_xdiff / 2;
13080         int border_right_diff = playfield_xdiff - border_left_diff;
13081
13082         vp_playfield->border_left  += border_left_diff;
13083         vp_playfield->border_right += border_right_diff;
13084       }
13085     }
13086
13087     if (is_playfield_mode &&
13088         dynamic_playfield_height &&
13089         vp_playfield->height != playfield_height_corrected)
13090     {
13091       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13092
13093       vp_playfield->height = playfield_height_corrected;
13094
13095       if (vp_playfield->valign == VALIGN_TOP)
13096       {
13097         vp_playfield->border_top += playfield_ydiff;
13098       }
13099       else if (vp_playfield->align == VALIGN_BOTTOM)
13100       {
13101         vp_playfield->border_right += playfield_ydiff;
13102       }
13103       else if (vp_playfield->align == VALIGN_MIDDLE)
13104       {
13105         int border_top_diff    = playfield_ydiff / 2;
13106         int border_bottom_diff = playfield_ydiff - border_top_diff;
13107
13108         vp_playfield->border_top    += border_top_diff;
13109         vp_playfield->border_bottom += border_bottom_diff;
13110       }
13111     }
13112
13113     // adjust door positions according to specified alignment
13114
13115     for (j = 0; j < 2; j++)
13116     {
13117       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13118
13119       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13120         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13121       else if (vp_door->align == ALIGN_CENTER)
13122         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13123       else if (vp_door->align == ALIGN_RIGHT)
13124         vp_door->x += vp_window->width - vp_door->width;
13125
13126       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13127         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13128       else if (vp_door->valign == VALIGN_MIDDLE)
13129         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13130       else if (vp_door->valign == VALIGN_BOTTOM)
13131         vp_door->y += vp_window->height - vp_door->height;
13132     }
13133   }
13134 }
13135
13136 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13137 {
13138   static struct
13139   {
13140     struct XYTileSize *dst, *src;
13141     int graphic;
13142   }
13143   editor_buttons_xy[] =
13144   {
13145     {
13146       &editor.button.element_left,      &editor.palette.element_left,
13147       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13148     },
13149     {
13150       &editor.button.element_middle,    &editor.palette.element_middle,
13151       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13152     },
13153     {
13154       &editor.button.element_right,     &editor.palette.element_right,
13155       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13156     },
13157
13158     { NULL,                     NULL                    }
13159   };
13160   int i;
13161
13162   // set default position for element buttons to element graphics
13163   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13164   {
13165     if ((*editor_buttons_xy[i].dst).x == -1 &&
13166         (*editor_buttons_xy[i].dst).y == -1)
13167     {
13168       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13169
13170       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13171
13172       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13173     }
13174   }
13175
13176   // adjust editor palette rows and columns if specified to be dynamic
13177
13178   if (editor.palette.cols == -1)
13179   {
13180     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13181     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13182     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13183
13184     editor.palette.cols = (vp_width - sc_width) / bt_width;
13185
13186     if (editor.palette.x == -1)
13187     {
13188       int palette_width = editor.palette.cols * bt_width + sc_width;
13189
13190       editor.palette.x = (vp_width - palette_width) / 2;
13191     }
13192   }
13193
13194   if (editor.palette.rows == -1)
13195   {
13196     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13197     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13198     int tx_height = getFontHeight(FONT_TEXT_2);
13199
13200     editor.palette.rows = (vp_height - tx_height) / bt_height;
13201
13202     if (editor.palette.y == -1)
13203     {
13204       int palette_height = editor.palette.rows * bt_height + tx_height;
13205
13206       editor.palette.y = (vp_height - palette_height) / 2;
13207     }
13208   }
13209 }
13210
13211 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13212                                                       boolean initialize)
13213 {
13214   // special case: check if network and preview player positions are redefined,
13215   // to compare this later against the main menu level preview being redefined
13216   struct TokenIntPtrInfo menu_config_players[] =
13217   {
13218     { "main.network_players.x", &menu.main.network_players.redefined    },
13219     { "main.network_players.y", &menu.main.network_players.redefined    },
13220     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13221     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13222     { "preview.x",              &preview.redefined                      },
13223     { "preview.y",              &preview.redefined                      }
13224   };
13225   int i;
13226
13227   if (initialize)
13228   {
13229     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13230       *menu_config_players[i].value = FALSE;
13231   }
13232   else
13233   {
13234     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13235       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13236         *menu_config_players[i].value = TRUE;
13237   }
13238 }
13239
13240 static void InitMenuDesignSettings_PreviewPlayers(void)
13241 {
13242   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13243 }
13244
13245 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13246 {
13247   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13248 }
13249
13250 static void LoadMenuDesignSettingsFromFilename(char *filename)
13251 {
13252   static struct TitleFadingInfo tfi;
13253   static struct TitleMessageInfo tmi;
13254   static struct TokenInfo title_tokens[] =
13255   {
13256     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13257     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13258     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13259     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13260     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13261
13262     { -1,               NULL,                   NULL                    }
13263   };
13264   static struct TokenInfo titlemessage_tokens[] =
13265   {
13266     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13267     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13268     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13269     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13270     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13271     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13272     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13273     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13274     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13275     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13276     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13277     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13278     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13279     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13280     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13281     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13282     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13283     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13284
13285     { -1,               NULL,                   NULL                    }
13286   };
13287   static struct
13288   {
13289     struct TitleFadingInfo *info;
13290     char *text;
13291   }
13292   title_info[] =
13293   {
13294     // initialize first titles from "enter screen" definitions, if defined
13295     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13296     { &title_first_default,             "menu.enter_screen.TITLE"       },
13297
13298     // initialize title screens from "next screen" definitions, if defined
13299     { &title_initial_default,           "menu.next_screen.TITLE"        },
13300     { &title_default,                   "menu.next_screen.TITLE"        },
13301
13302     { NULL,                             NULL                            }
13303   };
13304   static struct
13305   {
13306     struct TitleMessageInfo *array;
13307     char *text;
13308   }
13309   titlemessage_arrays[] =
13310   {
13311     // initialize first titles from "enter screen" definitions, if defined
13312     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13313     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13314     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13315     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13316
13317     // initialize titles from "next screen" definitions, if defined
13318     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13319     { titlescreen,                      "menu.next_screen.TITLE"        },
13320     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13321     { titlemessage,                     "menu.next_screen.TITLE"        },
13322
13323     // overwrite titles with title definitions, if defined
13324     { titlescreen_initial_first,        "[title_initial]"               },
13325     { titlescreen_first,                "[title]"                       },
13326     { titlemessage_initial_first,       "[title_initial]"               },
13327     { titlemessage_first,               "[title]"                       },
13328
13329     { titlescreen_initial,              "[title_initial]"               },
13330     { titlescreen,                      "[title]"                       },
13331     { titlemessage_initial,             "[title_initial]"               },
13332     { titlemessage,                     "[title]"                       },
13333
13334     // overwrite titles with title screen/message definitions, if defined
13335     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13336     { titlescreen_first,                "[titlescreen]"                 },
13337     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13338     { titlemessage_first,               "[titlemessage]"                },
13339
13340     { titlescreen_initial,              "[titlescreen_initial]"         },
13341     { titlescreen,                      "[titlescreen]"                 },
13342     { titlemessage_initial,             "[titlemessage_initial]"        },
13343     { titlemessage,                     "[titlemessage]"                },
13344
13345     { NULL,                             NULL                            }
13346   };
13347   SetupFileHash *setup_file_hash;
13348   int i, j, k;
13349
13350   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13351     return;
13352
13353   // the following initializes hierarchical values from dynamic configuration
13354
13355   // special case: initialize with default values that may be overwritten
13356   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13357   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13358   {
13359     struct TokenIntPtrInfo menu_config[] =
13360     {
13361       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13362       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13363       { "menu.list_size",       &menu.list_size[i]      }
13364     };
13365
13366     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13367     {
13368       char *token = menu_config[j].token;
13369       char *value = getHashEntry(setup_file_hash, token);
13370
13371       if (value != NULL)
13372         *menu_config[j].value = get_integer_from_string(value);
13373     }
13374   }
13375
13376   // special case: initialize with default values that may be overwritten
13377   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13378   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13379   {
13380     struct TokenIntPtrInfo menu_config[] =
13381     {
13382       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13383       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13384       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13385       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13386       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13387     };
13388
13389     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13390     {
13391       char *token = menu_config[j].token;
13392       char *value = getHashEntry(setup_file_hash, token);
13393
13394       if (value != NULL)
13395         *menu_config[j].value = get_integer_from_string(value);
13396     }
13397   }
13398
13399   // special case: initialize with default values that may be overwritten
13400   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13401   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13402   {
13403     struct TokenIntPtrInfo menu_config[] =
13404     {
13405       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13406       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13407     };
13408
13409     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13410     {
13411       char *token = menu_config[j].token;
13412       char *value = getHashEntry(setup_file_hash, token);
13413
13414       if (value != NULL)
13415         *menu_config[j].value = get_integer_from_string(value);
13416     }
13417   }
13418
13419   // special case: initialize with default values that may be overwritten
13420   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13421   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13422   {
13423     struct TokenIntPtrInfo menu_config[] =
13424     {
13425       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13426       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13427       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13428       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13429       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13430       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13431       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13432       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13433       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13434       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13435     };
13436
13437     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13438     {
13439       char *token = menu_config[j].token;
13440       char *value = getHashEntry(setup_file_hash, token);
13441
13442       if (value != NULL)
13443         *menu_config[j].value = get_integer_from_string(value);
13444     }
13445   }
13446
13447   // special case: initialize with default values that may be overwritten
13448   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13449   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13450   {
13451     struct TokenIntPtrInfo menu_config[] =
13452     {
13453       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13454       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13455       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13456       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13457       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13458       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13459       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13460       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13461       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13462     };
13463
13464     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13465     {
13466       char *token = menu_config[j].token;
13467       char *value = getHashEntry(setup_file_hash, token);
13468
13469       if (value != NULL)
13470         *menu_config[j].value = get_token_parameter_value(token, value);
13471     }
13472   }
13473
13474   // special case: initialize with default values that may be overwritten
13475   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13476   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13477   {
13478     struct
13479     {
13480       char *token_prefix;
13481       struct RectWithBorder *struct_ptr;
13482     }
13483     vp_struct[] =
13484     {
13485       { "viewport.window",      &viewport.window[i]     },
13486       { "viewport.playfield",   &viewport.playfield[i]  },
13487       { "viewport.door_1",      &viewport.door_1[i]     },
13488       { "viewport.door_2",      &viewport.door_2[i]     }
13489     };
13490
13491     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13492     {
13493       struct TokenIntPtrInfo vp_config[] =
13494       {
13495         { ".x",                 &vp_struct[j].struct_ptr->x             },
13496         { ".y",                 &vp_struct[j].struct_ptr->y             },
13497         { ".width",             &vp_struct[j].struct_ptr->width         },
13498         { ".height",            &vp_struct[j].struct_ptr->height        },
13499         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13500         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13501         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13502         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13503         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13504         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13505         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13506         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13507         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13508         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13509         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13510         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13511         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13512         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13513         { ".align",             &vp_struct[j].struct_ptr->align         },
13514         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13515       };
13516
13517       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13518       {
13519         char *token = getStringCat2(vp_struct[j].token_prefix,
13520                                     vp_config[k].token);
13521         char *value = getHashEntry(setup_file_hash, token);
13522
13523         if (value != NULL)
13524           *vp_config[k].value = get_token_parameter_value(token, value);
13525
13526         free(token);
13527       }
13528     }
13529   }
13530
13531   // special case: initialize with default values that may be overwritten
13532   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13533   for (i = 0; title_info[i].info != NULL; i++)
13534   {
13535     struct TitleFadingInfo *info = title_info[i].info;
13536     char *base_token = title_info[i].text;
13537
13538     for (j = 0; title_tokens[j].type != -1; j++)
13539     {
13540       char *token = getStringCat2(base_token, title_tokens[j].text);
13541       char *value = getHashEntry(setup_file_hash, token);
13542
13543       if (value != NULL)
13544       {
13545         int parameter_value = get_token_parameter_value(token, value);
13546
13547         tfi = *info;
13548
13549         *(int *)title_tokens[j].value = (int)parameter_value;
13550
13551         *info = tfi;
13552       }
13553
13554       free(token);
13555     }
13556   }
13557
13558   // special case: initialize with default values that may be overwritten
13559   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13560   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13561   {
13562     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13563     char *base_token = titlemessage_arrays[i].text;
13564
13565     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13566     {
13567       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13568       char *value = getHashEntry(setup_file_hash, token);
13569
13570       if (value != NULL)
13571       {
13572         int parameter_value = get_token_parameter_value(token, value);
13573
13574         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13575         {
13576           tmi = array[k];
13577
13578           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13579             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13580           else
13581             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13582
13583           array[k] = tmi;
13584         }
13585       }
13586
13587       free(token);
13588     }
13589   }
13590
13591   // read (and overwrite with) values that may be specified in config file
13592   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13593
13594   // special case: check if network and preview player positions are redefined
13595   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13596
13597   freeSetupFileHash(setup_file_hash);
13598 }
13599
13600 void LoadMenuDesignSettings(void)
13601 {
13602   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13603
13604   InitMenuDesignSettings_Static();
13605   InitMenuDesignSettings_SpecialPreProcessing();
13606   InitMenuDesignSettings_PreviewPlayers();
13607
13608   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13609   {
13610     // first look for special settings configured in level series config
13611     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13612
13613     if (fileExists(filename_base))
13614       LoadMenuDesignSettingsFromFilename(filename_base);
13615   }
13616
13617   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13618
13619   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13620     LoadMenuDesignSettingsFromFilename(filename_local);
13621
13622   InitMenuDesignSettings_SpecialPostProcessing();
13623 }
13624
13625 void LoadMenuDesignSettings_AfterGraphics(void)
13626 {
13627   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13628 }
13629
13630 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13631                                 boolean ignore_defaults)
13632 {
13633   int i;
13634
13635   for (i = 0; sound_config_vars[i].token != NULL; i++)
13636   {
13637     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13638
13639     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13640     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13641       continue;
13642
13643     if (value != NULL)
13644       *sound_config_vars[i].value =
13645         get_token_parameter_value(sound_config_vars[i].token, value);
13646   }
13647 }
13648
13649 void InitSoundSettings_Static(void)
13650 {
13651   // always start with reliable default values from static default config
13652   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13653 }
13654
13655 static void LoadSoundSettingsFromFilename(char *filename)
13656 {
13657   SetupFileHash *setup_file_hash;
13658
13659   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13660     return;
13661
13662   // read (and overwrite with) values that may be specified in config file
13663   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13664
13665   freeSetupFileHash(setup_file_hash);
13666 }
13667
13668 void LoadSoundSettings(void)
13669 {
13670   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13671
13672   InitSoundSettings_Static();
13673
13674   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13675   {
13676     // first look for special settings configured in level series config
13677     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13678
13679     if (fileExists(filename_base))
13680       LoadSoundSettingsFromFilename(filename_base);
13681   }
13682
13683   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13684
13685   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13686     LoadSoundSettingsFromFilename(filename_local);
13687 }
13688
13689 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13690 {
13691   char *filename = getEditorSetupFilename();
13692   SetupFileList *setup_file_list, *list;
13693   SetupFileHash *element_hash;
13694   int num_unknown_tokens = 0;
13695   int i;
13696
13697   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13698     return;
13699
13700   element_hash = newSetupFileHash();
13701
13702   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13703     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13704
13705   // determined size may be larger than needed (due to unknown elements)
13706   *num_elements = 0;
13707   for (list = setup_file_list; list != NULL; list = list->next)
13708     (*num_elements)++;
13709
13710   // add space for up to 3 more elements for padding that may be needed
13711   *num_elements += 3;
13712
13713   // free memory for old list of elements, if needed
13714   checked_free(*elements);
13715
13716   // allocate memory for new list of elements
13717   *elements = checked_malloc(*num_elements * sizeof(int));
13718
13719   *num_elements = 0;
13720   for (list = setup_file_list; list != NULL; list = list->next)
13721   {
13722     char *value = getHashEntry(element_hash, list->token);
13723
13724     if (value == NULL)          // try to find obsolete token mapping
13725     {
13726       char *mapped_token = get_mapped_token(list->token);
13727
13728       if (mapped_token != NULL)
13729       {
13730         value = getHashEntry(element_hash, mapped_token);
13731
13732         free(mapped_token);
13733       }
13734     }
13735
13736     if (value != NULL)
13737     {
13738       (*elements)[(*num_elements)++] = atoi(value);
13739     }
13740     else
13741     {
13742       if (num_unknown_tokens == 0)
13743       {
13744         Warn("---");
13745         Warn("unknown token(s) found in config file:");
13746         Warn("- config file: '%s'", filename);
13747
13748         num_unknown_tokens++;
13749       }
13750
13751       Warn("- token: '%s'", list->token);
13752     }
13753   }
13754
13755   if (num_unknown_tokens > 0)
13756     Warn("---");
13757
13758   while (*num_elements % 4)     // pad with empty elements, if needed
13759     (*elements)[(*num_elements)++] = EL_EMPTY;
13760
13761   freeSetupFileList(setup_file_list);
13762   freeSetupFileHash(element_hash);
13763
13764 #if 0
13765   for (i = 0; i < *num_elements; i++)
13766     Debug("editor", "element '%s' [%d]\n",
13767           element_info[(*elements)[i]].token_name, (*elements)[i]);
13768 #endif
13769 }
13770
13771 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13772                                                      boolean is_sound)
13773 {
13774   SetupFileHash *setup_file_hash = NULL;
13775   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13776   char *filename_music, *filename_prefix, *filename_info;
13777   struct
13778   {
13779     char *token;
13780     char **value_ptr;
13781   }
13782   token_to_value_ptr[] =
13783   {
13784     { "title_header",   &tmp_music_file_info.title_header       },
13785     { "artist_header",  &tmp_music_file_info.artist_header      },
13786     { "album_header",   &tmp_music_file_info.album_header       },
13787     { "year_header",    &tmp_music_file_info.year_header        },
13788     { "played_header",  &tmp_music_file_info.played_header      },
13789
13790     { "title",          &tmp_music_file_info.title              },
13791     { "artist",         &tmp_music_file_info.artist             },
13792     { "album",          &tmp_music_file_info.album              },
13793     { "year",           &tmp_music_file_info.year               },
13794     { "played",         &tmp_music_file_info.played             },
13795
13796     { NULL,             NULL                                    },
13797   };
13798   int i;
13799
13800   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13801                     getCustomMusicFilename(basename));
13802
13803   if (filename_music == NULL)
13804     return NULL;
13805
13806   // ---------- try to replace file extension ----------
13807
13808   filename_prefix = getStringCopy(filename_music);
13809   if (strrchr(filename_prefix, '.') != NULL)
13810     *strrchr(filename_prefix, '.') = '\0';
13811   filename_info = getStringCat2(filename_prefix, ".txt");
13812
13813   if (fileExists(filename_info))
13814     setup_file_hash = loadSetupFileHash(filename_info);
13815
13816   free(filename_prefix);
13817   free(filename_info);
13818
13819   if (setup_file_hash == NULL)
13820   {
13821     // ---------- try to add file extension ----------
13822
13823     filename_prefix = getStringCopy(filename_music);
13824     filename_info = getStringCat2(filename_prefix, ".txt");
13825
13826     if (fileExists(filename_info))
13827       setup_file_hash = loadSetupFileHash(filename_info);
13828
13829     free(filename_prefix);
13830     free(filename_info);
13831   }
13832
13833   if (setup_file_hash == NULL)
13834     return NULL;
13835
13836   // ---------- music file info found ----------
13837
13838   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13839
13840   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13841   {
13842     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13843
13844     *token_to_value_ptr[i].value_ptr =
13845       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13846   }
13847
13848   tmp_music_file_info.basename = getStringCopy(basename);
13849   tmp_music_file_info.music = music;
13850   tmp_music_file_info.is_sound = is_sound;
13851
13852   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13853   *new_music_file_info = tmp_music_file_info;
13854
13855   return new_music_file_info;
13856 }
13857
13858 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13859 {
13860   return get_music_file_info_ext(basename, music, FALSE);
13861 }
13862
13863 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13864 {
13865   return get_music_file_info_ext(basename, sound, TRUE);
13866 }
13867
13868 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13869                                      char *basename, boolean is_sound)
13870 {
13871   for (; list != NULL; list = list->next)
13872     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13873       return TRUE;
13874
13875   return FALSE;
13876 }
13877
13878 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13879 {
13880   return music_info_listed_ext(list, basename, FALSE);
13881 }
13882
13883 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13884 {
13885   return music_info_listed_ext(list, basename, TRUE);
13886 }
13887
13888 void LoadMusicInfo(void)
13889 {
13890   int num_music_noconf = getMusicListSize_NoConf();
13891   int num_music = getMusicListSize();
13892   int num_sounds = getSoundListSize();
13893   struct FileInfo *music, *sound;
13894   struct MusicFileInfo *next, **new;
13895
13896   int i;
13897
13898   while (music_file_info != NULL)
13899   {
13900     next = music_file_info->next;
13901
13902     checked_free(music_file_info->basename);
13903
13904     checked_free(music_file_info->title_header);
13905     checked_free(music_file_info->artist_header);
13906     checked_free(music_file_info->album_header);
13907     checked_free(music_file_info->year_header);
13908     checked_free(music_file_info->played_header);
13909
13910     checked_free(music_file_info->title);
13911     checked_free(music_file_info->artist);
13912     checked_free(music_file_info->album);
13913     checked_free(music_file_info->year);
13914     checked_free(music_file_info->played);
13915
13916     free(music_file_info);
13917
13918     music_file_info = next;
13919   }
13920
13921   new = &music_file_info;
13922
13923   // get (configured or unconfigured) music file info for all levels
13924   for (i = leveldir_current->first_level;
13925        i <= leveldir_current->last_level; i++)
13926   {
13927     int music_nr;
13928
13929     if (levelset.music[i] != MUS_UNDEFINED)
13930     {
13931       // get music file info for configured level music
13932       music_nr = levelset.music[i];
13933     }
13934     else if (num_music_noconf > 0)
13935     {
13936       // get music file info for unconfigured level music
13937       int level_pos = i - leveldir_current->first_level;
13938
13939       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13940     }
13941     else
13942     {
13943       continue;
13944     }
13945
13946     char *basename = getMusicInfoEntryFilename(music_nr);
13947
13948     if (basename == NULL)
13949       continue;
13950
13951     if (!music_info_listed(music_file_info, basename))
13952     {
13953       *new = get_music_file_info(basename, music_nr);
13954
13955       if (*new != NULL)
13956         new = &(*new)->next;
13957     }
13958   }
13959
13960   // get music file info for all remaining configured music files
13961   for (i = 0; i < num_music; i++)
13962   {
13963     music = getMusicListEntry(i);
13964
13965     if (music->filename == NULL)
13966       continue;
13967
13968     if (strEqual(music->filename, UNDEFINED_FILENAME))
13969       continue;
13970
13971     // a configured file may be not recognized as music
13972     if (!FileIsMusic(music->filename))
13973       continue;
13974
13975     if (!music_info_listed(music_file_info, music->filename))
13976     {
13977       *new = get_music_file_info(music->filename, i);
13978
13979       if (*new != NULL)
13980         new = &(*new)->next;
13981     }
13982   }
13983
13984   // get sound file info for all configured sound files
13985   for (i = 0; i < num_sounds; i++)
13986   {
13987     sound = getSoundListEntry(i);
13988
13989     if (sound->filename == NULL)
13990       continue;
13991
13992     if (strEqual(sound->filename, UNDEFINED_FILENAME))
13993       continue;
13994
13995     // a configured file may be not recognized as sound
13996     if (!FileIsSound(sound->filename))
13997       continue;
13998
13999     if (!sound_info_listed(music_file_info, sound->filename))
14000     {
14001       *new = get_sound_file_info(sound->filename, i);
14002       if (*new != NULL)
14003         new = &(*new)->next;
14004     }
14005   }
14006
14007   // add pointers to previous list nodes
14008
14009   struct MusicFileInfo *node = music_file_info;
14010
14011   while (node != NULL)
14012   {
14013     if (node->next)
14014       node->next->prev = node;
14015
14016     node = node->next;
14017   }
14018 }
14019
14020 static void add_helpanim_entry(int element, int action, int direction,
14021                                int delay, int *num_list_entries)
14022 {
14023   struct HelpAnimInfo *new_list_entry;
14024   (*num_list_entries)++;
14025
14026   helpanim_info =
14027     checked_realloc(helpanim_info,
14028                     *num_list_entries * sizeof(struct HelpAnimInfo));
14029   new_list_entry = &helpanim_info[*num_list_entries - 1];
14030
14031   new_list_entry->element = element;
14032   new_list_entry->action = action;
14033   new_list_entry->direction = direction;
14034   new_list_entry->delay = delay;
14035 }
14036
14037 static void print_unknown_token(char *filename, char *token, int token_nr)
14038 {
14039   if (token_nr == 0)
14040   {
14041     Warn("---");
14042     Warn("unknown token(s) found in config file:");
14043     Warn("- config file: '%s'", filename);
14044   }
14045
14046   Warn("- token: '%s'", token);
14047 }
14048
14049 static void print_unknown_token_end(int token_nr)
14050 {
14051   if (token_nr > 0)
14052     Warn("---");
14053 }
14054
14055 void LoadHelpAnimInfo(void)
14056 {
14057   char *filename = getHelpAnimFilename();
14058   SetupFileList *setup_file_list = NULL, *list;
14059   SetupFileHash *element_hash, *action_hash, *direction_hash;
14060   int num_list_entries = 0;
14061   int num_unknown_tokens = 0;
14062   int i;
14063
14064   if (fileExists(filename))
14065     setup_file_list = loadSetupFileList(filename);
14066
14067   if (setup_file_list == NULL)
14068   {
14069     // use reliable default values from static configuration
14070     SetupFileList *insert_ptr;
14071
14072     insert_ptr = setup_file_list =
14073       newSetupFileList(helpanim_config[0].token,
14074                        helpanim_config[0].value);
14075
14076     for (i = 1; helpanim_config[i].token; i++)
14077       insert_ptr = addListEntry(insert_ptr,
14078                                 helpanim_config[i].token,
14079                                 helpanim_config[i].value);
14080   }
14081
14082   element_hash   = newSetupFileHash();
14083   action_hash    = newSetupFileHash();
14084   direction_hash = newSetupFileHash();
14085
14086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14087     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14088
14089   for (i = 0; i < NUM_ACTIONS; i++)
14090     setHashEntry(action_hash, element_action_info[i].suffix,
14091                  i_to_a(element_action_info[i].value));
14092
14093   // do not store direction index (bit) here, but direction value!
14094   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14095     setHashEntry(direction_hash, element_direction_info[i].suffix,
14096                  i_to_a(1 << element_direction_info[i].value));
14097
14098   for (list = setup_file_list; list != NULL; list = list->next)
14099   {
14100     char *element_token, *action_token, *direction_token;
14101     char *element_value, *action_value, *direction_value;
14102     int delay = atoi(list->value);
14103
14104     if (strEqual(list->token, "end"))
14105     {
14106       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14107
14108       continue;
14109     }
14110
14111     /* first try to break element into element/action/direction parts;
14112        if this does not work, also accept combined "element[.act][.dir]"
14113        elements (like "dynamite.active"), which are unique elements */
14114
14115     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14116     {
14117       element_value = getHashEntry(element_hash, list->token);
14118       if (element_value != NULL)        // element found
14119         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14120                            &num_list_entries);
14121       else
14122       {
14123         // no further suffixes found -- this is not an element
14124         print_unknown_token(filename, list->token, num_unknown_tokens++);
14125       }
14126
14127       continue;
14128     }
14129
14130     // token has format "<prefix>.<something>"
14131
14132     action_token = strchr(list->token, '.');    // suffix may be action ...
14133     direction_token = action_token;             // ... or direction
14134
14135     element_token = getStringCopy(list->token);
14136     *strchr(element_token, '.') = '\0';
14137
14138     element_value = getHashEntry(element_hash, element_token);
14139
14140     if (element_value == NULL)          // this is no element
14141     {
14142       element_value = getHashEntry(element_hash, list->token);
14143       if (element_value != NULL)        // combined element found
14144         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14145                            &num_list_entries);
14146       else
14147         print_unknown_token(filename, list->token, num_unknown_tokens++);
14148
14149       free(element_token);
14150
14151       continue;
14152     }
14153
14154     action_value = getHashEntry(action_hash, action_token);
14155
14156     if (action_value != NULL)           // action found
14157     {
14158       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14159                     &num_list_entries);
14160
14161       free(element_token);
14162
14163       continue;
14164     }
14165
14166     direction_value = getHashEntry(direction_hash, direction_token);
14167
14168     if (direction_value != NULL)        // direction found
14169     {
14170       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14171                          &num_list_entries);
14172
14173       free(element_token);
14174
14175       continue;
14176     }
14177
14178     if (strchr(action_token + 1, '.') == NULL)
14179     {
14180       // no further suffixes found -- this is not an action nor direction
14181
14182       element_value = getHashEntry(element_hash, list->token);
14183       if (element_value != NULL)        // combined element found
14184         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14185                            &num_list_entries);
14186       else
14187         print_unknown_token(filename, list->token, num_unknown_tokens++);
14188
14189       free(element_token);
14190
14191       continue;
14192     }
14193
14194     // token has format "<prefix>.<suffix>.<something>"
14195
14196     direction_token = strchr(action_token + 1, '.');
14197
14198     action_token = getStringCopy(action_token);
14199     *strchr(action_token + 1, '.') = '\0';
14200
14201     action_value = getHashEntry(action_hash, action_token);
14202
14203     if (action_value == NULL)           // this is no action
14204     {
14205       element_value = getHashEntry(element_hash, list->token);
14206       if (element_value != NULL)        // combined element found
14207         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14208                            &num_list_entries);
14209       else
14210         print_unknown_token(filename, list->token, num_unknown_tokens++);
14211
14212       free(element_token);
14213       free(action_token);
14214
14215       continue;
14216     }
14217
14218     direction_value = getHashEntry(direction_hash, direction_token);
14219
14220     if (direction_value != NULL)        // direction found
14221     {
14222       add_helpanim_entry(atoi(element_value), atoi(action_value),
14223                          atoi(direction_value), delay, &num_list_entries);
14224
14225       free(element_token);
14226       free(action_token);
14227
14228       continue;
14229     }
14230
14231     // this is no direction
14232
14233     element_value = getHashEntry(element_hash, list->token);
14234     if (element_value != NULL)          // combined element found
14235       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14236                          &num_list_entries);
14237     else
14238       print_unknown_token(filename, list->token, num_unknown_tokens++);
14239
14240     free(element_token);
14241     free(action_token);
14242   }
14243
14244   print_unknown_token_end(num_unknown_tokens);
14245
14246   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14247   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14248
14249   freeSetupFileList(setup_file_list);
14250   freeSetupFileHash(element_hash);
14251   freeSetupFileHash(action_hash);
14252   freeSetupFileHash(direction_hash);
14253
14254 #if 0
14255   for (i = 0; i < num_list_entries; i++)
14256     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14257           EL_NAME(helpanim_info[i].element),
14258           helpanim_info[i].element,
14259           helpanim_info[i].action,
14260           helpanim_info[i].direction,
14261           helpanim_info[i].delay);
14262 #endif
14263 }
14264
14265 void LoadHelpTextInfo(void)
14266 {
14267   char *filename = getHelpTextFilename();
14268   int i;
14269
14270   if (helptext_info != NULL)
14271   {
14272     freeSetupFileHash(helptext_info);
14273     helptext_info = NULL;
14274   }
14275
14276   if (fileExists(filename))
14277     helptext_info = loadSetupFileHash(filename);
14278
14279   if (helptext_info == NULL)
14280   {
14281     // use reliable default values from static configuration
14282     helptext_info = newSetupFileHash();
14283
14284     for (i = 0; helptext_config[i].token; i++)
14285       setHashEntry(helptext_info,
14286                    helptext_config[i].token,
14287                    helptext_config[i].value);
14288   }
14289
14290 #if 0
14291   BEGIN_HASH_ITERATION(helptext_info, itr)
14292   {
14293     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14294           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14295   }
14296   END_HASH_ITERATION(hash, itr)
14297 #endif
14298 }
14299
14300
14301 // ----------------------------------------------------------------------------
14302 // convert levels
14303 // ----------------------------------------------------------------------------
14304
14305 #define MAX_NUM_CONVERT_LEVELS          1000
14306
14307 void ConvertLevels(void)
14308 {
14309   static LevelDirTree *convert_leveldir = NULL;
14310   static int convert_level_nr = -1;
14311   static int num_levels_handled = 0;
14312   static int num_levels_converted = 0;
14313   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14314   int i;
14315
14316   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14317                                                global.convert_leveldir);
14318
14319   if (convert_leveldir == NULL)
14320     Fail("no such level identifier: '%s'", global.convert_leveldir);
14321
14322   leveldir_current = convert_leveldir;
14323
14324   if (global.convert_level_nr != -1)
14325   {
14326     convert_leveldir->first_level = global.convert_level_nr;
14327     convert_leveldir->last_level  = global.convert_level_nr;
14328   }
14329
14330   convert_level_nr = convert_leveldir->first_level;
14331
14332   PrintLine("=", 79);
14333   Print("Converting levels\n");
14334   PrintLine("-", 79);
14335   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14336   Print("Level series name:       '%s'\n", convert_leveldir->name);
14337   Print("Level series author:     '%s'\n", convert_leveldir->author);
14338   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14339   PrintLine("=", 79);
14340   Print("\n");
14341
14342   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14343     levels_failed[i] = FALSE;
14344
14345   while (convert_level_nr <= convert_leveldir->last_level)
14346   {
14347     char *level_filename;
14348     boolean new_level;
14349
14350     level_nr = convert_level_nr++;
14351
14352     Print("Level %03d: ", level_nr);
14353
14354     LoadLevel(level_nr);
14355     if (level.no_level_file || level.no_valid_file)
14356     {
14357       Print("(no level)\n");
14358       continue;
14359     }
14360
14361     Print("converting level ... ");
14362
14363 #if 0
14364     // special case: conversion of some EMC levels as requested by ACME
14365     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14366 #endif
14367
14368     level_filename = getDefaultLevelFilename(level_nr);
14369     new_level = !fileExists(level_filename);
14370
14371     if (new_level)
14372     {
14373       SaveLevel(level_nr);
14374
14375       num_levels_converted++;
14376
14377       Print("converted.\n");
14378     }
14379     else
14380     {
14381       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14382         levels_failed[level_nr] = TRUE;
14383
14384       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14385     }
14386
14387     num_levels_handled++;
14388   }
14389
14390   Print("\n");
14391   PrintLine("=", 79);
14392   Print("Number of levels handled: %d\n", num_levels_handled);
14393   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14394          (num_levels_handled ?
14395           num_levels_converted * 100 / num_levels_handled : 0));
14396   PrintLine("-", 79);
14397   Print("Summary (for automatic parsing by scripts):\n");
14398   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14399          convert_leveldir->identifier, num_levels_converted,
14400          num_levels_handled,
14401          (num_levels_handled ?
14402           num_levels_converted * 100 / num_levels_handled : 0));
14403
14404   if (num_levels_handled != num_levels_converted)
14405   {
14406     Print(", FAILED:");
14407     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14408       if (levels_failed[i])
14409         Print(" %03d", i);
14410   }
14411
14412   Print("\n");
14413   PrintLine("=", 79);
14414
14415   CloseAllAndExit(0);
14416 }
14417
14418
14419 // ----------------------------------------------------------------------------
14420 // create and save images for use in level sketches (raw BMP format)
14421 // ----------------------------------------------------------------------------
14422
14423 void CreateLevelSketchImages(void)
14424 {
14425   Bitmap *bitmap1;
14426   Bitmap *bitmap2;
14427   int i;
14428
14429   InitElementPropertiesGfxElement();
14430
14431   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14432   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14433
14434   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14435   {
14436     int element = getMappedElement(i);
14437     char basename1[16];
14438     char basename2[16];
14439     char *filename1;
14440     char *filename2;
14441
14442     sprintf(basename1, "%04d.bmp", i);
14443     sprintf(basename2, "%04ds.bmp", i);
14444
14445     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14446     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14447
14448     DrawSizedElement(0, 0, element, TILESIZE);
14449     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14450
14451     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14452       Fail("cannot save level sketch image file '%s'", filename1);
14453
14454     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14455     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14456
14457     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14458       Fail("cannot save level sketch image file '%s'", filename2);
14459
14460     free(filename1);
14461     free(filename2);
14462
14463     // create corresponding SQL statements (for normal and small images)
14464     if (i < 1000)
14465     {
14466       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14467       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14468     }
14469
14470     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14471     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14472
14473     // optional: create content for forum level sketch demonstration post
14474     if (options.debug)
14475       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14476   }
14477
14478   FreeBitmap(bitmap1);
14479   FreeBitmap(bitmap2);
14480
14481   if (options.debug)
14482     fprintf(stderr, "\n");
14483
14484   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14485
14486   CloseAllAndExit(0);
14487 }
14488
14489
14490 // ----------------------------------------------------------------------------
14491 // create and save images for element collecting animations (raw BMP format)
14492 // ----------------------------------------------------------------------------
14493
14494 static boolean createCollectImage(int element)
14495 {
14496   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14497 }
14498
14499 void CreateCollectElementImages(void)
14500 {
14501   int i, j;
14502   int num_steps = 8;
14503   int anim_frames = num_steps - 1;
14504   int tile_size = TILESIZE;
14505   int anim_width  = tile_size * anim_frames;
14506   int anim_height = tile_size;
14507   int num_collect_images = 0;
14508   int pos_collect_images = 0;
14509
14510   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14511     if (createCollectImage(i))
14512       num_collect_images++;
14513
14514   Info("Creating %d element collecting animation images ...",
14515        num_collect_images);
14516
14517   int dst_width  = anim_width * 2;
14518   int dst_height = anim_height * num_collect_images / 2;
14519   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14520   char *basename_bmp = "RocksCollect.bmp";
14521   char *basename_png = "RocksCollect.png";
14522   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14523   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14524   int len_filename_bmp = strlen(filename_bmp);
14525   int len_filename_png = strlen(filename_png);
14526   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14527   char cmd_convert[max_command_len];
14528
14529   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14530            filename_bmp,
14531            filename_png);
14532
14533   // force using RGBA surface for destination bitmap
14534   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14535                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14536
14537   dst_bitmap->surface =
14538     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14539
14540   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14541   {
14542     if (!createCollectImage(i))
14543       continue;
14544
14545     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14546     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14547     int graphic = el2img(i);
14548     char *token_name = element_info[i].token_name;
14549     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14550     Bitmap *src_bitmap;
14551     int src_x, src_y;
14552
14553     Info("- creating collecting image for '%s' ...", token_name);
14554
14555     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14556
14557     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14558                tile_size, tile_size, 0, 0);
14559
14560     // force using RGBA surface for temporary bitmap (using transparent black)
14561     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14562                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14563
14564     tmp_bitmap->surface =
14565       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14566
14567     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14568
14569     for (j = 0; j < anim_frames; j++)
14570     {
14571       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14572       int frame_size = frame_size_final * num_steps;
14573       int offset = (tile_size - frame_size_final) / 2;
14574       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14575
14576       while (frame_size > frame_size_final)
14577       {
14578         frame_size /= 2;
14579
14580         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14581
14582         FreeBitmap(frame_bitmap);
14583
14584         frame_bitmap = half_bitmap;
14585       }
14586
14587       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14588                        frame_size_final, frame_size_final,
14589                        dst_x + j * tile_size + offset, dst_y + offset);
14590
14591       FreeBitmap(frame_bitmap);
14592     }
14593
14594     tmp_bitmap->surface_masked = NULL;
14595
14596     FreeBitmap(tmp_bitmap);
14597
14598     pos_collect_images++;
14599   }
14600
14601   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14602     Fail("cannot save element collecting image file '%s'", filename_bmp);
14603
14604   FreeBitmap(dst_bitmap);
14605
14606   Info("Converting image file from BMP to PNG ...");
14607
14608   if (system(cmd_convert) != 0)
14609     Fail("converting image file failed");
14610
14611   unlink(filename_bmp);
14612
14613   Info("Done.");
14614
14615   CloseAllAndExit(0);
14616 }
14617
14618
14619 // ----------------------------------------------------------------------------
14620 // create and save images for custom and group elements (raw BMP format)
14621 // ----------------------------------------------------------------------------
14622
14623 void CreateCustomElementImages(char *directory)
14624 {
14625   char *src_basename = "RocksCE-template.ilbm";
14626   char *dst_basename = "RocksCE.bmp";
14627   char *src_filename = getPath2(directory, src_basename);
14628   char *dst_filename = getPath2(directory, dst_basename);
14629   Bitmap *src_bitmap;
14630   Bitmap *bitmap;
14631   int yoffset_ce = 0;
14632   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14633   int i;
14634
14635   InitVideoDefaults();
14636
14637   ReCreateBitmap(&backbuffer, video.width, video.height);
14638
14639   src_bitmap = LoadImage(src_filename);
14640
14641   bitmap = CreateBitmap(TILEX * 16 * 2,
14642                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14643                         DEFAULT_DEPTH);
14644
14645   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14646   {
14647     int x = i % 16;
14648     int y = i / 16;
14649     int ii = i + 1;
14650     int j;
14651
14652     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14653                TILEX * x, TILEY * y + yoffset_ce);
14654
14655     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14656                TILEX, TILEY,
14657                TILEX * x + TILEX * 16,
14658                TILEY * y + yoffset_ce);
14659
14660     for (j = 2; j >= 0; j--)
14661     {
14662       int c = ii % 10;
14663
14664       BlitBitmap(src_bitmap, bitmap,
14665                  TILEX + c * 7, 0, 6, 10,
14666                  TILEX * x + 6 + j * 7,
14667                  TILEY * y + 11 + yoffset_ce);
14668
14669       BlitBitmap(src_bitmap, bitmap,
14670                  TILEX + c * 8, TILEY, 6, 10,
14671                  TILEX * 16 + TILEX * x + 6 + j * 8,
14672                  TILEY * y + 10 + yoffset_ce);
14673
14674       ii /= 10;
14675     }
14676   }
14677
14678   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14679   {
14680     int x = i % 16;
14681     int y = i / 16;
14682     int ii = i + 1;
14683     int j;
14684
14685     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14686                TILEX * x, TILEY * y + yoffset_ge);
14687
14688     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14689                TILEX, TILEY,
14690                TILEX * x + TILEX * 16,
14691                TILEY * y + yoffset_ge);
14692
14693     for (j = 1; j >= 0; j--)
14694     {
14695       int c = ii % 10;
14696
14697       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14698                  TILEX * x + 6 + j * 10,
14699                  TILEY * y + 11 + yoffset_ge);
14700
14701       BlitBitmap(src_bitmap, bitmap,
14702                  TILEX + c * 8, TILEY + 12, 6, 10,
14703                  TILEX * 16 + TILEX * x + 10 + j * 8,
14704                  TILEY * y + 10 + yoffset_ge);
14705
14706       ii /= 10;
14707     }
14708   }
14709
14710   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14711     Fail("cannot save CE graphics file '%s'", dst_filename);
14712
14713   FreeBitmap(bitmap);
14714
14715   CloseAllAndExit(0);
14716 }