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